arlinkauth 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 +131 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.js +305 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/react.d.ts +23 -0
- package/dist/react.js +37 -0
- package/dist/types.d.ts +95 -0
- package/dist/types.js +9 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# arlinkauth
|
|
2
|
+
|
|
3
|
+
Authentication SDK for Arweave apps. Sign in with GitHub or Google, get an Arweave wallet automatically.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install arlinkauth
|
|
9
|
+
# or
|
|
10
|
+
bun add arlinkauth
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createArlinkAuthClient } from 'arlinkauth';
|
|
17
|
+
|
|
18
|
+
const auth = createArlinkAuthClient({
|
|
19
|
+
apiUrl: 'https://your-worker.workers.dev'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Initialize (checks for existing session)
|
|
23
|
+
await auth.init();
|
|
24
|
+
|
|
25
|
+
// Login with GitHub (opens popup)
|
|
26
|
+
await auth.login();
|
|
27
|
+
// or
|
|
28
|
+
await auth.loginWithGoogle();
|
|
29
|
+
|
|
30
|
+
// Get current user
|
|
31
|
+
const user = auth.getState().user;
|
|
32
|
+
console.log(user.arweave_address);
|
|
33
|
+
|
|
34
|
+
// Sign and upload data to Arweave
|
|
35
|
+
const result = await auth.dispatch({
|
|
36
|
+
data: 'Hello Arweave',
|
|
37
|
+
tags: [{ name: 'Content-Type', value: 'text/plain' }]
|
|
38
|
+
});
|
|
39
|
+
console.log(result.id); // Transaction ID
|
|
40
|
+
|
|
41
|
+
// Logout
|
|
42
|
+
auth.logout();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## React Integration
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { AuthProvider, useAuth } from 'arlinkauth/react';
|
|
49
|
+
|
|
50
|
+
function App() {
|
|
51
|
+
return (
|
|
52
|
+
<AuthProvider apiUrl="https://your-worker.workers.dev">
|
|
53
|
+
<YourApp />
|
|
54
|
+
</AuthProvider>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function YourApp() {
|
|
59
|
+
const { user, login, loginWithGoogle, logout, client } = useAuth();
|
|
60
|
+
|
|
61
|
+
if (!user) {
|
|
62
|
+
return (
|
|
63
|
+
<div>
|
|
64
|
+
<button onClick={login}>Sign in with GitHub</button>
|
|
65
|
+
<button onClick={loginWithGoogle}>Sign in with Google</button>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div>
|
|
72
|
+
<p>Welcome, {user.name}</p>
|
|
73
|
+
<p>Arweave address: {user.arweave_address}</p>
|
|
74
|
+
<button onClick={logout}>Sign out</button>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Reference
|
|
81
|
+
|
|
82
|
+
### Client Methods
|
|
83
|
+
|
|
84
|
+
| Method | Description |
|
|
85
|
+
|--------|-------------|
|
|
86
|
+
| `init()` | Initialize client, check for existing session |
|
|
87
|
+
| `login()` | Login with GitHub (default) |
|
|
88
|
+
| `loginWithGithub()` | Login with GitHub |
|
|
89
|
+
| `loginWithGoogle()` | Login with Google |
|
|
90
|
+
| `logout()` | Clear session |
|
|
91
|
+
| `getState()` | Get current auth state |
|
|
92
|
+
| `getToken()` | Get JWT token |
|
|
93
|
+
| `isAuthenticated()` | Check if user is logged in |
|
|
94
|
+
| `onAuthChange(callback)` | Subscribe to auth state changes |
|
|
95
|
+
|
|
96
|
+
### Wallet Methods
|
|
97
|
+
|
|
98
|
+
| Method | Description |
|
|
99
|
+
|--------|-------------|
|
|
100
|
+
| `sign(input)` | Sign an Arweave L1 transaction |
|
|
101
|
+
| `signDataItem(input)` | Sign an ANS-104 data item |
|
|
102
|
+
| `signature(input)` | Create raw signature of data |
|
|
103
|
+
| `dispatch(input)` | Sign and upload to Arweave via bundler |
|
|
104
|
+
|
|
105
|
+
### Types
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
type ArlinkUser = {
|
|
109
|
+
id: string;
|
|
110
|
+
email: string | null;
|
|
111
|
+
name: string | null;
|
|
112
|
+
avatar_url: string | null;
|
|
113
|
+
github_id: number | null;
|
|
114
|
+
github_username: string | null;
|
|
115
|
+
google_id: string | null;
|
|
116
|
+
arweave_address: string | null;
|
|
117
|
+
created_at: string;
|
|
118
|
+
updated_at: string;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
type AuthState = {
|
|
122
|
+
user: ArlinkUser | null;
|
|
123
|
+
token: string | null;
|
|
124
|
+
isLoading: boolean;
|
|
125
|
+
isAuthenticated: boolean;
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type WauthClientOptions, type WauthUser, type AuthState, type AuthChangeListener, type OAuthProvider, type SignTransactionInput, type SignTransactionResult, type SignDataItemInput, type SignDataItemResult, type SignatureInput, type SignatureResult, type DispatchInput, type DispatchResult } from "./types.js";
|
|
2
|
+
export declare function createWauthClient(options: WauthClientOptions): {
|
|
3
|
+
init: () => Promise<AuthState>;
|
|
4
|
+
login: () => Promise<boolean>;
|
|
5
|
+
loginWithGithub: () => Promise<boolean>;
|
|
6
|
+
loginWithGoogle: () => Promise<boolean>;
|
|
7
|
+
loginWithProvider: (provider: OAuthProvider) => Promise<boolean>;
|
|
8
|
+
logout: () => void;
|
|
9
|
+
handleCallback: () => boolean;
|
|
10
|
+
isAuthenticated: () => boolean;
|
|
11
|
+
getToken: () => string | null;
|
|
12
|
+
getState: () => AuthState;
|
|
13
|
+
onAuthChange: (listener: AuthChangeListener) => () => void;
|
|
14
|
+
api: {
|
|
15
|
+
getMe: () => Promise<WauthUser>;
|
|
16
|
+
};
|
|
17
|
+
sign: (input: SignTransactionInput) => Promise<SignTransactionResult>;
|
|
18
|
+
signDataItem: (input: SignDataItemInput) => Promise<SignDataItemResult>;
|
|
19
|
+
signature: (input: SignatureInput) => Promise<SignatureResult>;
|
|
20
|
+
dispatch: (input: DispatchInput) => Promise<DispatchResult>;
|
|
21
|
+
};
|
|
22
|
+
export type WauthClient = ReturnType<typeof createWauthClient>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { WalletAction, } from "./types.js";
|
|
2
|
+
const DEFAULT_TOKEN_KEY = "arlinkauth_token";
|
|
3
|
+
export function createWauthClient(options) {
|
|
4
|
+
const { apiUrl } = options;
|
|
5
|
+
const tokenKey = options.tokenKey ?? DEFAULT_TOKEN_KEY;
|
|
6
|
+
const listeners = new Set();
|
|
7
|
+
let state = {
|
|
8
|
+
user: null,
|
|
9
|
+
token: getStoredToken(),
|
|
10
|
+
isLoading: false,
|
|
11
|
+
isAuthenticated: false,
|
|
12
|
+
};
|
|
13
|
+
// ── Token Storage ───────────────────────────────────
|
|
14
|
+
function getStoredToken() {
|
|
15
|
+
try {
|
|
16
|
+
return localStorage.getItem(tokenKey);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function setStoredToken(token) {
|
|
23
|
+
try {
|
|
24
|
+
localStorage.setItem(tokenKey, token);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// SSR or storage unavailable
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function removeStoredToken() {
|
|
31
|
+
try {
|
|
32
|
+
localStorage.removeItem(tokenKey);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// SSR or storage unavailable
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ── State Management ────────────────────────────────
|
|
39
|
+
function setState(partial) {
|
|
40
|
+
state = { ...state, ...partial };
|
|
41
|
+
for (const listener of listeners) {
|
|
42
|
+
listener(state);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function getState() {
|
|
46
|
+
return state;
|
|
47
|
+
}
|
|
48
|
+
function onAuthChange(listener) {
|
|
49
|
+
listeners.add(listener);
|
|
50
|
+
return () => {
|
|
51
|
+
listeners.delete(listener);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// ── Authenticated Fetch ─────────────────────────────
|
|
55
|
+
async function fetchApi(path, init) {
|
|
56
|
+
const token = state.token;
|
|
57
|
+
if (!token) {
|
|
58
|
+
throw new Error("Not authenticated");
|
|
59
|
+
}
|
|
60
|
+
const res = await fetch(`${apiUrl}${path}`, {
|
|
61
|
+
...init,
|
|
62
|
+
headers: {
|
|
63
|
+
...init?.headers,
|
|
64
|
+
Authorization: `Bearer ${token}`,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
if (res.status === 401) {
|
|
68
|
+
// Token is invalid/expired — clear it
|
|
69
|
+
removeStoredToken();
|
|
70
|
+
setState({ token: null, user: null, isAuthenticated: false });
|
|
71
|
+
throw new Error("Session expired");
|
|
72
|
+
}
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
const body = await res.json().catch(() => ({}));
|
|
75
|
+
throw new Error(body.error ?? `API error: ${res.status}`);
|
|
76
|
+
}
|
|
77
|
+
return res.json();
|
|
78
|
+
}
|
|
79
|
+
// ── Auth Actions ────────────────────────────────────
|
|
80
|
+
/** Open a popup for OAuth login with the specified provider */
|
|
81
|
+
function loginWithProvider(provider) {
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
const loginUrl = `${apiUrl}/auth/${provider}`;
|
|
84
|
+
const width = 500;
|
|
85
|
+
const height = 700;
|
|
86
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
87
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
88
|
+
const popup = window.open(loginUrl, "wauth-login", `width=${width},height=${height},left=${left},top=${top},popup=1`);
|
|
89
|
+
if (!popup) {
|
|
90
|
+
console.error("[arlinkauth] Failed to open popup - check popup blocker");
|
|
91
|
+
resolve(false);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Listen for message from popup
|
|
95
|
+
const handleMessage = async (event) => {
|
|
96
|
+
// Strict origin validation
|
|
97
|
+
if (event.origin !== new URL(apiUrl).origin)
|
|
98
|
+
return;
|
|
99
|
+
// Strict message structure validation
|
|
100
|
+
if (typeof event.data !== "object" || event.data === null)
|
|
101
|
+
return;
|
|
102
|
+
if (event.data.type !== "arlinkauth:callback")
|
|
103
|
+
return;
|
|
104
|
+
if (typeof event.data.token !== "string" || event.data.token.length === 0)
|
|
105
|
+
return;
|
|
106
|
+
// Basic JWT structure validation (header.payload.signature)
|
|
107
|
+
const tokenParts = event.data.token.split(".");
|
|
108
|
+
if (tokenParts.length !== 3)
|
|
109
|
+
return;
|
|
110
|
+
window.removeEventListener("message", handleMessage);
|
|
111
|
+
popup.close();
|
|
112
|
+
// Store token and fetch user
|
|
113
|
+
setStoredToken(event.data.token);
|
|
114
|
+
setState({ token: event.data.token, isLoading: true, isAuthenticated: true });
|
|
115
|
+
try {
|
|
116
|
+
const user = await api.getMe();
|
|
117
|
+
setState({ user, isLoading: false, isAuthenticated: true });
|
|
118
|
+
resolve(true);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
removeStoredToken();
|
|
122
|
+
setState({ token: null, user: null, isLoading: false, isAuthenticated: false });
|
|
123
|
+
resolve(false);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
window.addEventListener("message", handleMessage);
|
|
127
|
+
// Clean up if popup is closed without completing auth
|
|
128
|
+
const checkClosed = setInterval(() => {
|
|
129
|
+
if (popup.closed) {
|
|
130
|
+
clearInterval(checkClosed);
|
|
131
|
+
window.removeEventListener("message", handleMessage);
|
|
132
|
+
resolve(false);
|
|
133
|
+
}
|
|
134
|
+
}, 500);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/** Open a popup for GitHub OAuth login (no page refresh) */
|
|
138
|
+
function login() {
|
|
139
|
+
return loginWithProvider("github");
|
|
140
|
+
}
|
|
141
|
+
/** Open a popup for GitHub OAuth login */
|
|
142
|
+
function loginWithGithub() {
|
|
143
|
+
return loginWithProvider("github");
|
|
144
|
+
}
|
|
145
|
+
/** Open a popup for Google OAuth login */
|
|
146
|
+
function loginWithGoogle() {
|
|
147
|
+
return loginWithProvider("google");
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check for existing token in localStorage.
|
|
151
|
+
* (URL-based callback is no longer used - popup flow uses postMessage)
|
|
152
|
+
*/
|
|
153
|
+
function handleCallback() {
|
|
154
|
+
const existing = getStoredToken();
|
|
155
|
+
if (existing) {
|
|
156
|
+
setState({ token: existing, isAuthenticated: true });
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
/** Clear the token and user state */
|
|
162
|
+
function logout() {
|
|
163
|
+
removeStoredToken();
|
|
164
|
+
setState({ token: null, user: null, isAuthenticated: false });
|
|
165
|
+
}
|
|
166
|
+
function isAuthenticated() {
|
|
167
|
+
return state.isAuthenticated;
|
|
168
|
+
}
|
|
169
|
+
function getToken() {
|
|
170
|
+
return state.token;
|
|
171
|
+
}
|
|
172
|
+
// ── API Methods ─────────────────────────────────────
|
|
173
|
+
const api = {
|
|
174
|
+
getMe: () => fetchApi("/api/me"),
|
|
175
|
+
};
|
|
176
|
+
// ── Wallet Signing Methods ─────────────────────────────
|
|
177
|
+
/**
|
|
178
|
+
* Sign an Arweave transaction.
|
|
179
|
+
* The transaction must be created using arweave-js and passed as the raw object.
|
|
180
|
+
*/
|
|
181
|
+
async function sign(input) {
|
|
182
|
+
return fetchApi("/api/wallet/action", {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: { "Content-Type": "application/json" },
|
|
185
|
+
body: JSON.stringify({
|
|
186
|
+
action: WalletAction.SIGN,
|
|
187
|
+
payload: { transaction: input.transaction },
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Sign an ANS-104 data item (for AO/bundled transactions).
|
|
193
|
+
* Returns the signed data item ID and raw bytes.
|
|
194
|
+
*/
|
|
195
|
+
async function signDataItem(input) {
|
|
196
|
+
// Convert Uint8Array to array for JSON serialization
|
|
197
|
+
const data = input.data instanceof Uint8Array
|
|
198
|
+
? Array.from(input.data)
|
|
199
|
+
: input.data;
|
|
200
|
+
return fetchApi("/api/wallet/action", {
|
|
201
|
+
method: "POST",
|
|
202
|
+
headers: { "Content-Type": "application/json" },
|
|
203
|
+
body: JSON.stringify({
|
|
204
|
+
action: WalletAction.SIGN_DATA_ITEM,
|
|
205
|
+
payload: {
|
|
206
|
+
dataItem: {
|
|
207
|
+
data,
|
|
208
|
+
tags: input.tags,
|
|
209
|
+
target: input.target,
|
|
210
|
+
anchor: input.anchor,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
}),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Create a raw signature of arbitrary data.
|
|
218
|
+
* Returns the raw signature bytes.
|
|
219
|
+
*/
|
|
220
|
+
async function signature(input) {
|
|
221
|
+
// Convert Uint8Array to array for JSON serialization
|
|
222
|
+
const data = input.data instanceof Uint8Array
|
|
223
|
+
? Array.from(input.data)
|
|
224
|
+
: input.data;
|
|
225
|
+
return fetchApi("/api/wallet/action", {
|
|
226
|
+
method: "POST",
|
|
227
|
+
headers: { "Content-Type": "application/json" },
|
|
228
|
+
body: JSON.stringify({
|
|
229
|
+
action: WalletAction.SIGNATURE,
|
|
230
|
+
payload: { data },
|
|
231
|
+
}),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Sign and dispatch a data item to Arweave via a bundler (Turbo/Irys).
|
|
236
|
+
* Returns the transaction ID and bundler response.
|
|
237
|
+
*/
|
|
238
|
+
async function dispatch(input) {
|
|
239
|
+
// Convert Uint8Array to array for JSON serialization
|
|
240
|
+
const data = input.data instanceof Uint8Array
|
|
241
|
+
? Array.from(input.data)
|
|
242
|
+
: input.data;
|
|
243
|
+
return fetchApi("/api/wallet/action", {
|
|
244
|
+
method: "POST",
|
|
245
|
+
headers: { "Content-Type": "application/json" },
|
|
246
|
+
body: JSON.stringify({
|
|
247
|
+
action: WalletAction.DISPATCH,
|
|
248
|
+
payload: {
|
|
249
|
+
data,
|
|
250
|
+
tags: input.tags,
|
|
251
|
+
target: input.target,
|
|
252
|
+
anchor: input.anchor,
|
|
253
|
+
bundler: input.bundler,
|
|
254
|
+
},
|
|
255
|
+
}),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
// ── Initialize ──────────────────────────────────────
|
|
259
|
+
/**
|
|
260
|
+
* Initialize the client: check for existing session token
|
|
261
|
+
* and validate it by fetching /api/me.
|
|
262
|
+
*/
|
|
263
|
+
async function init() {
|
|
264
|
+
handleCallback();
|
|
265
|
+
if (!state.token) {
|
|
266
|
+
setState({ isLoading: false, isAuthenticated: false });
|
|
267
|
+
return getState();
|
|
268
|
+
}
|
|
269
|
+
setState({ isLoading: true });
|
|
270
|
+
try {
|
|
271
|
+
const user = await api.getMe();
|
|
272
|
+
setState({ user, isLoading: false, isAuthenticated: true });
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Token invalid — clear everything
|
|
276
|
+
removeStoredToken();
|
|
277
|
+
setState({
|
|
278
|
+
token: null,
|
|
279
|
+
user: null,
|
|
280
|
+
isLoading: false,
|
|
281
|
+
isAuthenticated: false,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return getState();
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
init,
|
|
288
|
+
login,
|
|
289
|
+
loginWithGithub,
|
|
290
|
+
loginWithGoogle,
|
|
291
|
+
loginWithProvider,
|
|
292
|
+
logout,
|
|
293
|
+
handleCallback,
|
|
294
|
+
isAuthenticated,
|
|
295
|
+
getToken,
|
|
296
|
+
getState,
|
|
297
|
+
onAuthChange,
|
|
298
|
+
api,
|
|
299
|
+
// Wallet signing methods
|
|
300
|
+
sign,
|
|
301
|
+
signDataItem,
|
|
302
|
+
signature,
|
|
303
|
+
dispatch,
|
|
304
|
+
};
|
|
305
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { createWauthClient, createWauthClient as createArlinkAuthClient } from "./client.js";
|
|
2
|
+
export type { WauthClient, WauthClient as ArlinkAuthClient } from "./client.js";
|
|
3
|
+
export { WalletAction, type WauthUser, type WauthUser as ArlinkUser, type WauthClientOptions, type WauthClientOptions as ArlinkAuthClientOptions, type AuthState, type AuthChangeListener, type OAuthProvider, type ArweaveTag, type SignTransactionInput, type SignTransactionResult, type SignDataItemInput, type SignDataItemResult, type SignatureInput, type SignatureResult, type BundlerType, type DispatchInput, type DispatchResult, } from "./types.js";
|
package/dist/index.js
ADDED
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type WauthClient } from "./client.js";
|
|
3
|
+
import type { AuthState, WauthClientOptions, WauthUser } from "./types.js";
|
|
4
|
+
type AuthContextValue = {
|
|
5
|
+
user: WauthUser | null;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
isAuthenticated: boolean;
|
|
8
|
+
login: () => void;
|
|
9
|
+
loginWithGithub: () => void;
|
|
10
|
+
loginWithGoogle: () => void;
|
|
11
|
+
logout: () => void;
|
|
12
|
+
getToken: () => string | null;
|
|
13
|
+
client: WauthClient;
|
|
14
|
+
api: WauthClient["api"];
|
|
15
|
+
};
|
|
16
|
+
type AuthProviderProps = WauthClientOptions & {
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
};
|
|
19
|
+
export declare function AuthProvider({ children, ...options }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function useAuth(): AuthContextValue;
|
|
21
|
+
export type { WauthUser, AuthState, WauthClientOptions };
|
|
22
|
+
export type { OAuthProvider, ArweaveTag, SignTransactionInput, SignTransactionResult, SignDataItemInput, SignDataItemResult, SignatureInput, SignatureResult, BundlerType, DispatchInput, DispatchResult, } from "./types.js";
|
|
23
|
+
export { WalletAction } from "./types.js";
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useState, useMemo, } from "react";
|
|
3
|
+
import { createWauthClient } from "./client.js";
|
|
4
|
+
const AuthContext = createContext(null);
|
|
5
|
+
export function AuthProvider({ children, ...options }) {
|
|
6
|
+
const client = useMemo(() => createWauthClient(options), [options.apiUrl, options.tokenKey]);
|
|
7
|
+
const [authState, setAuthState] = useState(client.getState);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
// Subscribe to state changes
|
|
10
|
+
const unsubscribe = client.onAuthChange(setAuthState);
|
|
11
|
+
// Initialize: handle callback token + validate session
|
|
12
|
+
client.init();
|
|
13
|
+
return unsubscribe;
|
|
14
|
+
}, [client]);
|
|
15
|
+
const value = useMemo(() => ({
|
|
16
|
+
user: authState.user,
|
|
17
|
+
isLoading: authState.isLoading,
|
|
18
|
+
isAuthenticated: authState.isAuthenticated,
|
|
19
|
+
login: client.login,
|
|
20
|
+
loginWithGithub: client.loginWithGithub,
|
|
21
|
+
loginWithGoogle: client.loginWithGoogle,
|
|
22
|
+
logout: client.logout,
|
|
23
|
+
getToken: client.getToken,
|
|
24
|
+
client,
|
|
25
|
+
api: client.api,
|
|
26
|
+
}), [authState, client]);
|
|
27
|
+
return _jsx(AuthContext.Provider, { value: value, children: children });
|
|
28
|
+
}
|
|
29
|
+
// ── Hook ──────────────────────────────────────────────
|
|
30
|
+
export function useAuth() {
|
|
31
|
+
const ctx = useContext(AuthContext);
|
|
32
|
+
if (!ctx) {
|
|
33
|
+
throw new Error("useAuth must be used within an <AuthProvider>");
|
|
34
|
+
}
|
|
35
|
+
return ctx;
|
|
36
|
+
}
|
|
37
|
+
export { WalletAction } from "./types.js";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export type WauthUser = {
|
|
2
|
+
id: string;
|
|
3
|
+
email: string | null;
|
|
4
|
+
name: string | null;
|
|
5
|
+
avatar_url: string | null;
|
|
6
|
+
github_id: number | null;
|
|
7
|
+
github_username: string | null;
|
|
8
|
+
google_id: string | null;
|
|
9
|
+
arweave_address: string | null;
|
|
10
|
+
created_at: string;
|
|
11
|
+
updated_at: string;
|
|
12
|
+
};
|
|
13
|
+
export type OAuthProvider = "github" | "google";
|
|
14
|
+
export declare enum WalletAction {
|
|
15
|
+
SIGN = "sign",
|
|
16
|
+
SIGN_DATA_ITEM = "signDataItem",
|
|
17
|
+
SIGNATURE = "signature",
|
|
18
|
+
DISPATCH = "dispatch"
|
|
19
|
+
}
|
|
20
|
+
/** Tag for Arweave transactions/data items */
|
|
21
|
+
export type ArweaveTag = {
|
|
22
|
+
name: string;
|
|
23
|
+
value: string;
|
|
24
|
+
};
|
|
25
|
+
/** Input for signing an Arweave transaction */
|
|
26
|
+
export type SignTransactionInput = {
|
|
27
|
+
/** Raw transaction object from arweave-js */
|
|
28
|
+
transaction: unknown;
|
|
29
|
+
};
|
|
30
|
+
/** Result of signing a transaction */
|
|
31
|
+
export type SignTransactionResult = {
|
|
32
|
+
/** Signed transaction JSON */
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
};
|
|
35
|
+
/** Input for signing an ANS-104 data item */
|
|
36
|
+
export type SignDataItemInput = {
|
|
37
|
+
data: string | Uint8Array | number[];
|
|
38
|
+
tags?: ArweaveTag[];
|
|
39
|
+
target?: string;
|
|
40
|
+
anchor?: string;
|
|
41
|
+
};
|
|
42
|
+
/** Result of signing a data item */
|
|
43
|
+
export type SignDataItemResult = {
|
|
44
|
+
/** Data item ID (transaction ID) */
|
|
45
|
+
id: string;
|
|
46
|
+
/** Raw signed data item bytes */
|
|
47
|
+
raw: number[];
|
|
48
|
+
};
|
|
49
|
+
/** Input for raw signature */
|
|
50
|
+
export type SignatureInput = {
|
|
51
|
+
/** Data to sign - can be string, Uint8Array, or array of numbers */
|
|
52
|
+
data: string | Uint8Array | number[];
|
|
53
|
+
};
|
|
54
|
+
/** Result of raw signature */
|
|
55
|
+
export type SignatureResult = {
|
|
56
|
+
/** Raw signature bytes */
|
|
57
|
+
signature: number[];
|
|
58
|
+
};
|
|
59
|
+
/** Bundler types for dispatching data items */
|
|
60
|
+
export type BundlerType = "turbo" | "irys";
|
|
61
|
+
/** Input for dispatching a data item to Arweave via bundler */
|
|
62
|
+
export type DispatchInput = {
|
|
63
|
+
/** Data to upload - can be string or array of bytes */
|
|
64
|
+
data: string | Uint8Array | number[];
|
|
65
|
+
/** Optional tags for the data item */
|
|
66
|
+
tags?: ArweaveTag[];
|
|
67
|
+
/** Optional target address */
|
|
68
|
+
target?: string;
|
|
69
|
+
/** Optional anchor */
|
|
70
|
+
anchor?: string;
|
|
71
|
+
/** Which bundler to use (default: "turbo") */
|
|
72
|
+
bundler?: BundlerType;
|
|
73
|
+
};
|
|
74
|
+
/** Result of dispatching a data item */
|
|
75
|
+
export type DispatchResult = {
|
|
76
|
+
/** Transaction/data item ID */
|
|
77
|
+
id: string;
|
|
78
|
+
/** Which bundler was used */
|
|
79
|
+
bundler: BundlerType;
|
|
80
|
+
/** Response from the bundler */
|
|
81
|
+
response: Record<string, unknown>;
|
|
82
|
+
};
|
|
83
|
+
export type WauthClientOptions = {
|
|
84
|
+
/** Base URL of the wauth worker (e.g. "https://wauth.example.workers.dev") */
|
|
85
|
+
apiUrl: string;
|
|
86
|
+
/** localStorage key for the JWT. Default: "wauth_token" */
|
|
87
|
+
tokenKey?: string;
|
|
88
|
+
};
|
|
89
|
+
export type AuthState = {
|
|
90
|
+
user: WauthUser | null;
|
|
91
|
+
token: string | null;
|
|
92
|
+
isLoading: boolean;
|
|
93
|
+
isAuthenticated: boolean;
|
|
94
|
+
};
|
|
95
|
+
export type AuthChangeListener = (state: AuthState) => void;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ── API Response Types ──────────────────────────────────
|
|
2
|
+
// ── Wallet Action Types ──────────────────────────────────
|
|
3
|
+
export var WalletAction;
|
|
4
|
+
(function (WalletAction) {
|
|
5
|
+
WalletAction["SIGN"] = "sign";
|
|
6
|
+
WalletAction["SIGN_DATA_ITEM"] = "signDataItem";
|
|
7
|
+
WalletAction["SIGNATURE"] = "signature";
|
|
8
|
+
WalletAction["DISPATCH"] = "dispatch";
|
|
9
|
+
})(WalletAction || (WalletAction = {}));
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "arlinkauth",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Authentication SDK for Arweave apps - OAuth login with automatic wallet generation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./react": {
|
|
14
|
+
"types": "./dist/react.d.ts",
|
|
15
|
+
"import": "./dist/react.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"dev": "tsc --watch --preserveWatchOutput",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"arweave",
|
|
29
|
+
"auth",
|
|
30
|
+
"oauth",
|
|
31
|
+
"wallet",
|
|
32
|
+
"ao",
|
|
33
|
+
"arlink"
|
|
34
|
+
],
|
|
35
|
+
"author": "Ankush Singh",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/ankushKun/arlinkauth.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/ankushKun/arlinkauth/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/ankushKun/arlinkauth#readme",
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": ">=18"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"react": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/react": "^19",
|
|
55
|
+
"typescript": "^5"
|
|
56
|
+
}
|
|
57
|
+
}
|