@kraki/head 0.10.4 → 0.11.1
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/dist/account-api.d.ts +35 -0
- package/dist/account-api.js +254 -0
- package/dist/account-api.js.map +1 -0
- package/dist/auth-backend.d.ts +93 -0
- package/dist/auth-backend.js +11 -0
- package/dist/auth-backend.js.map +1 -0
- package/dist/cli.js +251 -58
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/ip-geo.d.ts +22 -0
- package/dist/ip-geo.js +89 -0
- package/dist/ip-geo.js.map +1 -0
- package/dist/local-auth-backend.d.ts +109 -0
- package/dist/local-auth-backend.js +421 -0
- package/dist/local-auth-backend.js.map +1 -0
- package/dist/remote-auth-backend.d.ts +48 -0
- package/dist/remote-auth-backend.js +89 -0
- package/dist/remote-auth-backend.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +154 -24
- package/dist/server.js.map +1 -1
- package/dist/storage.d.ts +44 -1
- package/dist/storage.js +220 -9
- package/dist/storage.js.map +1 -1
- package/package.json +2 -3
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account API — REST endpoints for auth delegation.
|
|
3
|
+
*
|
|
4
|
+
* Exposed by head in standalone mode so other (remote) heads can delegate
|
|
5
|
+
* auth to this instance. Handles: auth, challenge-response, pairing, config.
|
|
6
|
+
*
|
|
7
|
+
* Secured with a service API key (SERVICE_KEY / --service-key).
|
|
8
|
+
*/
|
|
9
|
+
import type { IncomingMessage, ServerResponse } from 'http';
|
|
10
|
+
import type { LocalAuthBackend } from './local-auth-backend.js';
|
|
11
|
+
export interface AccountApiOptions {
|
|
12
|
+
authBackend: LocalAuthBackend;
|
|
13
|
+
serviceKey?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class AccountApi {
|
|
16
|
+
private backend;
|
|
17
|
+
private serviceKey?;
|
|
18
|
+
constructor(options: AccountApiOptions);
|
|
19
|
+
/**
|
|
20
|
+
* Handle an HTTP request if it matches an account API route.
|
|
21
|
+
* Returns true if the request was handled, false otherwise.
|
|
22
|
+
*/
|
|
23
|
+
handleRequest(req: IncomingMessage, res: ServerResponse): Promise<boolean>;
|
|
24
|
+
private handleGetRegions;
|
|
25
|
+
private handleResolveLogin;
|
|
26
|
+
private handleAuth;
|
|
27
|
+
private handleChallenge;
|
|
28
|
+
private handleVerify;
|
|
29
|
+
private handleCreatePairing;
|
|
30
|
+
private handleRequestPairing;
|
|
31
|
+
private handleGetConfig;
|
|
32
|
+
private handleEdgeJoin;
|
|
33
|
+
private checkServiceKey;
|
|
34
|
+
private json;
|
|
35
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account API — REST endpoints for auth delegation.
|
|
3
|
+
*
|
|
4
|
+
* Exposed by head in standalone mode so other (remote) heads can delegate
|
|
5
|
+
* auth to this instance. Handles: auth, challenge-response, pairing, config.
|
|
6
|
+
*
|
|
7
|
+
* Secured with a service API key (SERVICE_KEY / --service-key).
|
|
8
|
+
*/
|
|
9
|
+
import { safeEqual } from './auth.js';
|
|
10
|
+
import { getLogger } from './logger.js';
|
|
11
|
+
export class AccountApi {
|
|
12
|
+
backend;
|
|
13
|
+
serviceKey;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.backend = options.authBackend;
|
|
16
|
+
this.serviceKey = options.serviceKey;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Handle an HTTP request if it matches an account API route.
|
|
20
|
+
* Returns true if the request was handled, false otherwise.
|
|
21
|
+
*/
|
|
22
|
+
async handleRequest(req, res) {
|
|
23
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
24
|
+
const path = url.pathname;
|
|
25
|
+
if (!path.startsWith('/api/'))
|
|
26
|
+
return false;
|
|
27
|
+
// CORS for all API routes
|
|
28
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
29
|
+
res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type');
|
|
30
|
+
if (req.method === 'OPTIONS') {
|
|
31
|
+
res.writeHead(204);
|
|
32
|
+
res.end();
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const publicRoute = (path === '/api/regions' && req.method === 'GET')
|
|
36
|
+
|| (path === '/api/login/resolve' && req.method === 'POST')
|
|
37
|
+
|| (path === '/api/edge/join' && req.method === 'POST');
|
|
38
|
+
if (!publicRoute && !this.checkServiceKey(req, res))
|
|
39
|
+
return true;
|
|
40
|
+
try {
|
|
41
|
+
switch (path) {
|
|
42
|
+
case '/api/regions':
|
|
43
|
+
if (req.method === 'GET')
|
|
44
|
+
return this.handleGetRegions(req, res);
|
|
45
|
+
break;
|
|
46
|
+
case '/api/login/resolve':
|
|
47
|
+
if (req.method === 'POST')
|
|
48
|
+
return await this.handleResolveLogin(req, res);
|
|
49
|
+
break;
|
|
50
|
+
case '/api/auth':
|
|
51
|
+
if (req.method === 'POST')
|
|
52
|
+
return await this.handleAuth(req, res);
|
|
53
|
+
break;
|
|
54
|
+
case '/api/auth/challenge':
|
|
55
|
+
if (req.method === 'POST')
|
|
56
|
+
return await this.handleChallenge(req, res);
|
|
57
|
+
break;
|
|
58
|
+
case '/api/auth/verify':
|
|
59
|
+
if (req.method === 'POST')
|
|
60
|
+
return await this.handleVerify(req, res);
|
|
61
|
+
break;
|
|
62
|
+
case '/api/pairing/create':
|
|
63
|
+
if (req.method === 'POST')
|
|
64
|
+
return await this.handleCreatePairing(req, res);
|
|
65
|
+
break;
|
|
66
|
+
case '/api/pairing/request':
|
|
67
|
+
if (req.method === 'POST')
|
|
68
|
+
return await this.handleRequestPairing(req, res);
|
|
69
|
+
break;
|
|
70
|
+
case '/api/config':
|
|
71
|
+
if (req.method === 'GET')
|
|
72
|
+
return this.handleGetConfig(req, res);
|
|
73
|
+
break;
|
|
74
|
+
case '/api/edge/join':
|
|
75
|
+
if (req.method === 'POST')
|
|
76
|
+
return await this.handleEdgeJoin(req, res);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
getLogger().error('Account API error', { path, error: err.message });
|
|
82
|
+
this.json(res, 500, { ok: false, code: 'internal_error', message: 'Internal server error' });
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
// ── Route handlers ────────────────────────────────────
|
|
88
|
+
handleGetRegions(_req, res) {
|
|
89
|
+
this.json(res, 200, {
|
|
90
|
+
version: this.backend.getRegionVersion(),
|
|
91
|
+
ttlSec: 300,
|
|
92
|
+
regions: this.backend.getRegions(),
|
|
93
|
+
});
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
async handleResolveLogin(req, res) {
|
|
97
|
+
const body = await readBody(req);
|
|
98
|
+
if (!body?.auth) {
|
|
99
|
+
this.json(res, 400, { ok: false, code: 'bad_request', message: 'auth is required' });
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
const clientIp = req.headers['x-forwarded-for']?.toString().split(',')[0].trim()
|
|
103
|
+
?? req.socket?.remoteAddress;
|
|
104
|
+
const result = await this.backend.resolveLogin(body.auth, body.preferredRegion, clientIp);
|
|
105
|
+
if (!result.ok) {
|
|
106
|
+
const status = result.code === 'unknown_region' ? 400 : 401;
|
|
107
|
+
this.json(res, status, result);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.json(res, 200, result);
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
async handleAuth(req, res) {
|
|
115
|
+
const body = await readBody(req);
|
|
116
|
+
if (!body?.auth || !body?.device) {
|
|
117
|
+
this.json(res, 400, { ok: false, code: 'bad_request', message: 'auth and device required' });
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
const result = await this.backend.authenticate(body.auth, body.device, body.headRegion);
|
|
121
|
+
if (!result.ok && result.code === 'wrong_region') {
|
|
122
|
+
this.json(res, 403, result);
|
|
123
|
+
}
|
|
124
|
+
else if (!result.ok) {
|
|
125
|
+
this.json(res, 401, result);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.json(res, 200, result);
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
async handleChallenge(req, res) {
|
|
133
|
+
const body = await readBody(req);
|
|
134
|
+
if (!body?.deviceId) {
|
|
135
|
+
this.json(res, 400, { ok: false, code: 'bad_request', message: 'deviceId required' });
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
const result = await this.backend.startChallenge(body.deviceId, body.encryptionKey, body.headRegion);
|
|
139
|
+
if (!result.ok && result.code === 'wrong_region') {
|
|
140
|
+
this.json(res, 403, result);
|
|
141
|
+
}
|
|
142
|
+
else if (!result.ok) {
|
|
143
|
+
this.json(res, 401, result);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.json(res, 200, result);
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
async handleVerify(req, res) {
|
|
151
|
+
const body = await readBody(req);
|
|
152
|
+
if (!body?.deviceId || !body?.nonce || !body?.signature) {
|
|
153
|
+
this.json(res, 400, { ok: false, code: 'bad_request', message: 'deviceId, nonce, signature required' });
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
const result = await this.backend.verifyChallenge(body.deviceId, body.nonce, body.signature, body.encryptionKey, body.headRegion);
|
|
157
|
+
if (!result.ok && result.code === 'wrong_region') {
|
|
158
|
+
this.json(res, 403, result);
|
|
159
|
+
}
|
|
160
|
+
else if (!result.ok) {
|
|
161
|
+
this.json(res, 401, result);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
this.json(res, 200, result);
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
async handleCreatePairing(req, res) {
|
|
169
|
+
const body = await readBody(req);
|
|
170
|
+
if (!body?.userId) {
|
|
171
|
+
this.json(res, 400, { ok: false, code: 'bad_request', message: 'userId required' });
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
const result = this.backend.createPairingToken(body.userId);
|
|
175
|
+
this.json(res, 200, result);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
async handleRequestPairing(req, res) {
|
|
179
|
+
const body = await readBody(req);
|
|
180
|
+
if (body?.userId) {
|
|
181
|
+
// Direct create (authenticated user)
|
|
182
|
+
const result = this.backend.createPairingToken(body.userId);
|
|
183
|
+
this.json(res, 200, { ok: true, ...result });
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
if (body?.token) {
|
|
187
|
+
// One-shot: authenticate + create
|
|
188
|
+
const result = await this.backend.requestPairingToken(body.token, body.ip);
|
|
189
|
+
if (!result.ok) {
|
|
190
|
+
this.json(res, 401, result);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
this.json(res, 200, result);
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
this.json(res, 400, { ok: false, code: 'bad_request', message: 'userId or token required' });
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
handleGetConfig(_req, res) {
|
|
201
|
+
this.json(res, 200, this.backend.getAuthInfo());
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
async handleEdgeJoin(req, res) {
|
|
205
|
+
const body = await readBody(req);
|
|
206
|
+
if (!body?.token || !body?.region || !body?.relayUrl) {
|
|
207
|
+
this.json(res, 400, { ok: false, code: 'bad_request', message: 'token, region, and relayUrl are required' });
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
const result = this.backend.completeEdgeJoin(body.token, body.region, body.relayUrl, body.displayName);
|
|
211
|
+
if (!result.ok) {
|
|
212
|
+
const status = result.code === 'join_token_expired' ? 410 : 401;
|
|
213
|
+
this.json(res, status, result);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
this.json(res, 200, result);
|
|
217
|
+
}
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
// ── Helpers ───────────────────────────────────────────
|
|
221
|
+
checkServiceKey(req, res) {
|
|
222
|
+
const authHeader = req.headers.authorization ?? '';
|
|
223
|
+
const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : '';
|
|
224
|
+
const valid = !!token && ((this.serviceKey ? safeEqual(token, this.serviceKey) : false)
|
|
225
|
+
|| this.backend.validateServiceKey(token).valid);
|
|
226
|
+
if (!valid) {
|
|
227
|
+
this.json(res, 401, { ok: false, code: 'unauthorized', message: 'Invalid service key' });
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
json(res, status, data) {
|
|
233
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
234
|
+
res.end(JSON.stringify(data));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/** Read JSON body from request. */
|
|
238
|
+
function readBody(req) {
|
|
239
|
+
return new Promise((resolve) => {
|
|
240
|
+
const chunks = [];
|
|
241
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
242
|
+
req.on('end', () => {
|
|
243
|
+
try {
|
|
244
|
+
const text = Buffer.concat(chunks).toString();
|
|
245
|
+
resolve(text ? JSON.parse(text) : null);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
resolve(null);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
req.on('error', () => resolve(null));
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=account-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-api.js","sourceRoot":"","sources":["../src/account-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth backend abstraction for HeadServer.
|
|
3
|
+
*
|
|
4
|
+
* LocalAuthBackend: validates credentials directly using auth providers + Storage.
|
|
5
|
+
* RemoteAuthBackend: delegates auth to a remote Account Service via REST.
|
|
6
|
+
*
|
|
7
|
+
* HeadServer uses this interface so it doesn't care whether auth happens
|
|
8
|
+
* locally or remotely — same code path for both standalone and connected mode.
|
|
9
|
+
*/
|
|
10
|
+
import type { AuthMethod, DeviceInfo, DeviceSummary, UnicastEnvelope } from '@kraki/protocol';
|
|
11
|
+
export interface AuthResult {
|
|
12
|
+
ok: true;
|
|
13
|
+
userId: string;
|
|
14
|
+
deviceId: string;
|
|
15
|
+
authMethod: AuthMethod['method'];
|
|
16
|
+
user: {
|
|
17
|
+
id: string;
|
|
18
|
+
login: string;
|
|
19
|
+
provider: string;
|
|
20
|
+
email?: string;
|
|
21
|
+
preferences?: Record<string, unknown>;
|
|
22
|
+
region?: string;
|
|
23
|
+
};
|
|
24
|
+
devices: DeviceSummary[];
|
|
25
|
+
pendingMessages: UnicastEnvelope[];
|
|
26
|
+
githubClientId?: string;
|
|
27
|
+
vapidPublicKey?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface AuthChallengeResult {
|
|
30
|
+
ok: true;
|
|
31
|
+
nonce: string;
|
|
32
|
+
userId: string;
|
|
33
|
+
deviceId: string;
|
|
34
|
+
}
|
|
35
|
+
export interface AuthRegionMismatch {
|
|
36
|
+
ok: false;
|
|
37
|
+
code: 'wrong_region';
|
|
38
|
+
redirect?: string;
|
|
39
|
+
deviceId?: string;
|
|
40
|
+
message: string;
|
|
41
|
+
}
|
|
42
|
+
export interface AuthError {
|
|
43
|
+
ok: false;
|
|
44
|
+
code: string;
|
|
45
|
+
message: string;
|
|
46
|
+
}
|
|
47
|
+
export type AuthOutcome = AuthResult | AuthRegionMismatch | AuthError;
|
|
48
|
+
export type ChallengeOutcome = AuthChallengeResult | AuthRegionMismatch | AuthError;
|
|
49
|
+
export interface AuthBackend {
|
|
50
|
+
/**
|
|
51
|
+
* Full auth flow (token-based methods: github_token, github_oauth, apikey, open, pairing).
|
|
52
|
+
* Returns the full auth result including device list and pending messages.
|
|
53
|
+
*/
|
|
54
|
+
authenticate(auth: AuthMethod, device: DeviceInfo, headRegion?: string): Promise<AuthOutcome>;
|
|
55
|
+
/**
|
|
56
|
+
* Start challenge-response auth (step 1).
|
|
57
|
+
* Returns a nonce to be signed by the device.
|
|
58
|
+
*/
|
|
59
|
+
startChallenge(deviceId: string, encryptionKey?: string, headRegion?: string): Promise<ChallengeOutcome>;
|
|
60
|
+
/**
|
|
61
|
+
* Complete challenge-response auth (step 2).
|
|
62
|
+
* Verifies the signed nonce and returns the full auth result.
|
|
63
|
+
*/
|
|
64
|
+
verifyChallenge(deviceId: string, nonce: string, signature: string, encryptionKey?: string, headRegion?: string): Promise<AuthOutcome>;
|
|
65
|
+
/**
|
|
66
|
+
* Create a pairing token for an authenticated user.
|
|
67
|
+
* Returns the token string and TTL.
|
|
68
|
+
*/
|
|
69
|
+
createPairingToken(userId: string): {
|
|
70
|
+
token: string;
|
|
71
|
+
expiresIn: number;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Request a one-shot pairing token (authenticates the requester first).
|
|
75
|
+
* Used by the tentacle before the user has a device registered.
|
|
76
|
+
*/
|
|
77
|
+
requestPairingToken(token: string, ip?: string): Promise<{
|
|
78
|
+
ok: true;
|
|
79
|
+
userId: string;
|
|
80
|
+
pairingToken: string;
|
|
81
|
+
expiresIn: number;
|
|
82
|
+
} | AuthError>;
|
|
83
|
+
/**
|
|
84
|
+
* Get config info for auth_info responses.
|
|
85
|
+
* Returns available auth methods, GitHub client ID, VAPID key, etc.
|
|
86
|
+
*/
|
|
87
|
+
getAuthInfo(): AuthInfoConfig;
|
|
88
|
+
}
|
|
89
|
+
export interface AuthInfoConfig {
|
|
90
|
+
methods: string[];
|
|
91
|
+
githubClientId?: string;
|
|
92
|
+
vapidPublicKey?: string;
|
|
93
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth backend abstraction for HeadServer.
|
|
3
|
+
*
|
|
4
|
+
* LocalAuthBackend: validates credentials directly using auth providers + Storage.
|
|
5
|
+
* RemoteAuthBackend: delegates auth to a remote Account Service via REST.
|
|
6
|
+
*
|
|
7
|
+
* HeadServer uses this interface so it doesn't care whether auth happens
|
|
8
|
+
* locally or remotely — same code path for both standalone and connected mode.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=auth-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-backend.js","sourceRoot":"","sources":["../src/auth-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|