@tapflowio/relay 0.24.0 → 0.25.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/dist/RelayServer.d.ts +45 -0
- package/dist/RelayServer.d.ts.map +1 -1
- package/dist/RelayServer.js +245 -30
- package/dist/RelayServer.js.map +1 -1
- package/dist/api/apps.d.ts.map +1 -1
- package/dist/api/apps.js +6 -16
- package/dist/api/apps.js.map +1 -1
- package/dist/api/auth.d.ts.map +1 -1
- package/dist/api/auth.js +3 -12
- package/dist/api/auth.js.map +1 -1
- package/dist/api/builds.d.ts.map +1 -1
- package/dist/api/builds.js +7 -5
- package/dist/api/builds.js.map +1 -1
- package/dist/api/comments.d.ts.map +1 -1
- package/dist/api/comments.js +41 -15
- package/dist/api/comments.js.map +1 -1
- package/dist/api/invitations.d.ts +1 -1
- package/dist/api/invitations.d.ts.map +1 -1
- package/dist/api/invitations.js +7 -3
- package/dist/api/invitations.js.map +1 -1
- package/dist/api/passwordReset.js +2 -2
- package/dist/api/team.d.ts +2 -2
- package/dist/api/team.d.ts.map +1 -1
- package/dist/api/team.js +6 -2
- package/dist/api/team.js.map +1 -1
- package/dist/api/tokens.d.ts +1 -1
- package/dist/api/tokens.d.ts.map +1 -1
- package/dist/api/tokens.js +26 -7
- package/dist/api/tokens.js.map +1 -1
- package/dist/api/webhooks.d.ts.map +1 -1
- package/dist/api/webhooks.js +11 -5
- package/dist/api/webhooks.js.map +1 -1
- package/dist/lib/clientAddress.d.ts +7 -0
- package/dist/lib/clientAddress.d.ts.map +1 -1
- package/dist/lib/clientAddress.js +15 -0
- package/dist/lib/clientAddress.js.map +1 -1
- package/dist/lib/connectionAuth.d.ts +57 -2
- package/dist/lib/connectionAuth.d.ts.map +1 -1
- package/dist/lib/connectionAuth.js +65 -6
- package/dist/lib/connectionAuth.js.map +1 -1
- package/dist/middleware/auth.d.ts +44 -4
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +99 -27
- package/dist/middleware/auth.js.map +1 -1
- package/package.json +3 -3
- package/public/assets/AppCenter-D_1rFI40.js +26 -0
- package/public/assets/AppCenter-D_1rFI40.js.br +0 -0
- package/public/assets/AppCenter-D_1rFI40.js.gz +0 -0
- package/public/assets/Default-DF_X2_3I.js +1 -0
- package/public/assets/Default-DF_X2_3I.js.br +0 -0
- package/public/assets/Default-DF_X2_3I.js.gz +0 -0
- package/public/assets/{Invite-EVMTsuNz.js → Invite-DETTWdYp.js} +1 -1
- package/public/assets/Invite-DETTWdYp.js.br +0 -0
- package/public/assets/Invite-DETTWdYp.js.gz +0 -0
- package/public/assets/{MacResources-fCn40Tf5.js → MacResources--1Enisxs.js} +1 -1
- package/public/assets/MacResources--1Enisxs.js.br +0 -0
- package/public/assets/MacResources--1Enisxs.js.gz +0 -0
- package/public/assets/{QASession-CL-eyFxG.js → QASession-BjoD9Toj.js} +3 -3
- package/public/assets/QASession-BjoD9Toj.js.br +0 -0
- package/public/assets/QASession-BjoD9Toj.js.gz +0 -0
- package/public/assets/{ResetPassword-D-SFg4ap.js → ResetPassword-B9miyXTP.js} +1 -1
- package/public/assets/ResetPassword-B9miyXTP.js.br +0 -0
- package/public/assets/ResetPassword-B9miyXTP.js.gz +0 -0
- package/public/assets/{Setup-ULjvIfAi.js → Setup-CywZCqHU.js} +1 -1
- package/public/assets/Setup-CywZCqHU.js.br +0 -0
- package/public/assets/Setup-CywZCqHU.js.gz +0 -0
- package/public/assets/{Team-DH91GD4w.js → Team-CxdX_9mY.js} +2 -2
- package/public/assets/Team-CxdX_9mY.js.br +0 -0
- package/public/assets/Team-CxdX_9mY.js.gz +0 -0
- package/public/assets/Tokens-BQnnawvG.js +1 -0
- package/public/assets/Tokens-BQnnawvG.js.br +0 -0
- package/public/assets/Tokens-BQnnawvG.js.gz +0 -0
- package/public/assets/alert-dialog-CuPdBkxF.js +1 -0
- package/public/assets/alert-dialog-CuPdBkxF.js.br +0 -0
- package/public/assets/alert-dialog-CuPdBkxF.js.gz +0 -0
- package/public/assets/{badge-B4QAWrzA.js → badge-CSVsKOK_.js} +1 -1
- package/public/assets/badge-CSVsKOK_.js.br +0 -0
- package/public/assets/badge-CSVsKOK_.js.gz +0 -0
- package/public/assets/{build-format-CtmRXihN.js → build-format-l9BrlbZl.js} +1 -1
- package/public/assets/build-format-l9BrlbZl.js.br +0 -0
- package/public/assets/build-format-l9BrlbZl.js.gz +0 -0
- package/public/assets/{dialog-BAFN_ONP.js → dialog-B6jt7-2f.js} +3 -3
- package/public/assets/dialog-B6jt7-2f.js.br +0 -0
- package/public/assets/dialog-B6jt7-2f.js.gz +0 -0
- package/public/assets/{index-COOWQXpW.js → index-Cm17I31a.js} +32 -32
- package/public/assets/index-Cm17I31a.js.br +0 -0
- package/public/assets/index-Cm17I31a.js.gz +0 -0
- package/public/assets/index-D_P99bPX.css +1 -0
- package/public/assets/index-D_P99bPX.css.br +0 -0
- package/public/assets/{index-ozCdBYxk.css.gz → index-D_P99bPX.css.gz} +0 -0
- package/public/assets/{list-view-Rrqp8TkG.js → list-view-Bp6pvsde.js} +1 -1
- package/public/assets/list-view-Bp6pvsde.js.br +0 -0
- package/public/assets/list-view-Bp6pvsde.js.gz +0 -0
- package/public/assets/{pencil-CAHyK7Qf.js → pencil-g-WiK1F2.js} +1 -1
- package/public/assets/pencil-g-WiK1F2.js.br +0 -0
- package/public/assets/pencil-g-WiK1F2.js.gz +0 -0
- package/public/assets/resource-health-BbeyA8an.js +1 -0
- package/public/assets/resource-health-BbeyA8an.js.br +0 -0
- package/public/assets/resource-health-BbeyA8an.js.gz +0 -0
- package/public/assets/{trash-CkO-5TRA.js → trash-DHPg15P4.js} +1 -1
- package/public/assets/trash-DHPg15P4.js.br +0 -0
- package/public/assets/trash-DHPg15P4.js.gz +0 -0
- package/public/index.html +2 -2
- package/public/index.html.br +0 -0
- package/public/index.html.gz +0 -0
- package/public/assets/AppCenter-BHz6vh5p.js +0 -26
- package/public/assets/AppCenter-BHz6vh5p.js.br +0 -0
- package/public/assets/AppCenter-BHz6vh5p.js.gz +0 -0
- package/public/assets/Default-bCqU0Qz9.js +0 -1
- package/public/assets/Default-bCqU0Qz9.js.br +0 -0
- package/public/assets/Default-bCqU0Qz9.js.gz +0 -0
- package/public/assets/Invite-EVMTsuNz.js.br +0 -0
- package/public/assets/Invite-EVMTsuNz.js.gz +0 -0
- package/public/assets/MacResources-fCn40Tf5.js.br +0 -0
- package/public/assets/MacResources-fCn40Tf5.js.gz +0 -0
- package/public/assets/QASession-CL-eyFxG.js.br +0 -0
- package/public/assets/QASession-CL-eyFxG.js.gz +0 -0
- package/public/assets/ResetPassword-D-SFg4ap.js.br +0 -0
- package/public/assets/ResetPassword-D-SFg4ap.js.gz +0 -0
- package/public/assets/Setup-ULjvIfAi.js.br +0 -0
- package/public/assets/Setup-ULjvIfAi.js.gz +0 -0
- package/public/assets/Team-DH91GD4w.js.br +0 -0
- package/public/assets/Team-DH91GD4w.js.gz +0 -0
- package/public/assets/Tokens-DImLpOVZ.js +0 -1
- package/public/assets/Tokens-DImLpOVZ.js.br +0 -0
- package/public/assets/Tokens-DImLpOVZ.js.gz +0 -0
- package/public/assets/alert-dialog-CwcUyDZQ.js +0 -1
- package/public/assets/alert-dialog-CwcUyDZQ.js.br +0 -0
- package/public/assets/alert-dialog-CwcUyDZQ.js.gz +0 -0
- package/public/assets/badge-B4QAWrzA.js.br +0 -0
- package/public/assets/badge-B4QAWrzA.js.gz +0 -0
- package/public/assets/build-format-CtmRXihN.js.br +0 -0
- package/public/assets/build-format-CtmRXihN.js.gz +0 -0
- package/public/assets/dialog-BAFN_ONP.js.br +0 -0
- package/public/assets/dialog-BAFN_ONP.js.gz +0 -0
- package/public/assets/index-COOWQXpW.js.br +0 -0
- package/public/assets/index-COOWQXpW.js.gz +0 -0
- package/public/assets/index-ozCdBYxk.css +0 -1
- package/public/assets/index-ozCdBYxk.css.br +0 -0
- package/public/assets/list-view-Rrqp8TkG.js.br +0 -0
- package/public/assets/list-view-Rrqp8TkG.js.gz +0 -0
- package/public/assets/pencil-CAHyK7Qf.js.br +0 -0
- package/public/assets/pencil-CAHyK7Qf.js.gz +0 -0
- package/public/assets/resource-health-BRpSwarg.js +0 -1
- package/public/assets/resource-health-BRpSwarg.js.br +0 -0
- package/public/assets/resource-health-BRpSwarg.js.gz +0 -0
- package/public/assets/trash-CkO-5TRA.js.br +0 -0
- package/public/assets/trash-CkO-5TRA.js.gz +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientAddress.d.ts","sourceRoot":"","sources":["../../src/lib/clientAddress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAmB5B,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAMrE;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,GAAG,qBAAqB,CAG5F;AA6BD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,IAAI,CAEjE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,OAAO,CAElE"}
|
|
1
|
+
{"version":3,"file":"clientAddress.d.ts","sourceRoot":"","sources":["../../src/lib/clientAddress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAmB5B,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAMrE;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,GAAG,qBAAqB,CAG5F;AA6BD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,IAAI,CAEjE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,OAAO,CAElE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,qBAAqB,CAQ/G"}
|
|
@@ -64,4 +64,19 @@ export function markTunnelIngress(req) {
|
|
|
64
64
|
export function isTunnelIngress(req) {
|
|
65
65
|
return tunnelRequests.has(req);
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Locality of an HTTP request, for every door that serves only the relay host (`auth/init`,
|
|
69
|
+
* `auth/status`, `/api/v1/logs`). The trusted-proxy list is an argument rather than read from config
|
|
70
|
+
* here, so each caller passes the same list the WebSocket path uses: one locality rule, not one per
|
|
71
|
+
* route. The tunnel listener's mark is read here, so no caller can forget it.
|
|
72
|
+
*/
|
|
73
|
+
export function resolveRequestClient(req, trustedProxies) {
|
|
74
|
+
const xff = req.headers['x-forwarded-for'];
|
|
75
|
+
return resolveClientAddress({
|
|
76
|
+
socketAddr: req.socket.remoteAddress ?? '',
|
|
77
|
+
forwardedFor: Array.isArray(xff) ? xff[0] : xff,
|
|
78
|
+
trustedProxies,
|
|
79
|
+
viaTunnel: isTunnelIngress(req),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
67
82
|
//# sourceMappingURL=clientAddress.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientAddress.js","sourceRoot":"","sources":["../../src/lib/clientAddress.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"clientAddress.js","sourceRoot":"","sources":["../../src/lib/clientAddress.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
export declare const AGENT_SCOPE = "agent";
|
|
2
|
+
export declare const VIEW_SCOPE = "view";
|
|
2
3
|
export declare const WS_REJECT_REASON = "Unauthorized: agents need a PAT with 'agent' scope (--token / TAPFLOW_AGENT_TOKEN); browsers must sign in";
|
|
4
|
+
/** A PAT with neither `view` nor `agent`: it may upload builds, not open device sessions. */
|
|
5
|
+
export declare const WS_SCOPE_REASON = "Forbidden: this token lacks the 'view' scope needed for device sessions; create an API-type token";
|
|
6
|
+
/** An `agent` token is issued by an Admin and is only as good as that Admin role, checked at every use. */
|
|
7
|
+
export declare const WS_AGENT_OWNER_REASON = "Unauthorized: this agent token's owner is no longer an Admin; an Admin must issue a new agent token";
|
|
8
|
+
/** An open socket whose token was revoked, expired, or went with its owner. */
|
|
9
|
+
export declare const WS_TOKEN_GONE_REASON = "Unauthorized: this token was revoked or has expired, or its owner was removed";
|
|
3
10
|
export interface ConnectionAuthInput {
|
|
4
11
|
isLocal: boolean;
|
|
5
12
|
hasCookieAuth: boolean;
|
|
6
13
|
/** Scopes of a valid PAT on the upgrade request, or null when none was presented. */
|
|
7
14
|
patScopes: string[] | null;
|
|
15
|
+
/** The PAT owner's current role (from the users table), or null when no PAT was presented. */
|
|
16
|
+
patOwnerRole: string | null;
|
|
8
17
|
}
|
|
9
18
|
export type ConnectionDecision = {
|
|
10
19
|
action: 'reject';
|
|
@@ -13,10 +22,56 @@ export type ConnectionDecision = {
|
|
|
13
22
|
action: 'accept';
|
|
14
23
|
role: 'browser';
|
|
15
24
|
}
|
|
16
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* 첫 메시지(agent:register / stream:register)가 역할을 결정한다. What the first message may make of
|
|
27
|
+
* the socket is decided here, with the credentials: `mayBrowse` gates the fallback to `browser`, and
|
|
28
|
+
* `mayRegisterAgent` gates the two handshakes. A local socket may do both.
|
|
29
|
+
*/
|
|
17
30
|
| {
|
|
18
31
|
action: 'accept';
|
|
19
32
|
role: 'first-message';
|
|
33
|
+
mayBrowse: boolean;
|
|
34
|
+
mayRegisterAgent: boolean;
|
|
20
35
|
};
|
|
21
|
-
export declare function classifyConnection({ isLocal, hasCookieAuth, patScopes }: ConnectionAuthInput): ConnectionDecision;
|
|
36
|
+
export declare function classifyConnection({ isLocal, hasCookieAuth, patScopes, patOwnerRole }: ConnectionAuthInput): ConnectionDecision;
|
|
37
|
+
/**
|
|
38
|
+
* The credential a remote socket was accepted with. Recorded once at the handshake and re-checked
|
|
39
|
+
* against the database for as long as the socket is open. Local sockets have none: they needed no
|
|
40
|
+
* credential, so there is nothing to revoke.
|
|
41
|
+
*/
|
|
42
|
+
export type SocketPrincipal = {
|
|
43
|
+
via: 'cookie';
|
|
44
|
+
userId: number; /** JWT `exp`, seconds */
|
|
45
|
+
jwtExp?: number;
|
|
46
|
+
}
|
|
47
|
+
/** The PAT's id, never its hash: the hash is a bearer secret, the id is only a row. */
|
|
48
|
+
| {
|
|
49
|
+
via: 'pat';
|
|
50
|
+
userId: number;
|
|
51
|
+
patId: number;
|
|
52
|
+
};
|
|
53
|
+
/** What the database says now about a principal's credential. */
|
|
54
|
+
export interface CurrentCredential {
|
|
55
|
+
/** Cookie principals: the user row still exists. */
|
|
56
|
+
userExists: boolean;
|
|
57
|
+
/** PAT principals: the token row, unexpired, with its owner — or null when revoked, expired or cascaded. */
|
|
58
|
+
pat: {
|
|
59
|
+
scopes: string[];
|
|
60
|
+
ownerRole: string;
|
|
61
|
+
} | null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Would this socket still be accepted, given what the database says now? Returns the close reason, or
|
|
65
|
+
* null when it would.
|
|
66
|
+
*
|
|
67
|
+
* **One predicate instead of one rule per event.** Removal, demotion, revocation, expiry and an
|
|
68
|
+
* invitation accept that changes a role all reach an open socket through the same question, asked on
|
|
69
|
+
* the heartbeat and again right after any of those writes. A list of "which sockets does this event
|
|
70
|
+
* affect" is the design this replaced, and it had already missed expiry and the invitation path.
|
|
71
|
+
*
|
|
72
|
+
* `role` is what the socket has become: `agent` and `stream` rest on the `agent` scope and its owner's
|
|
73
|
+
* Admin role; `browser` rests on `view`. A socket that has not sent its first frame yet (`undefined`)
|
|
74
|
+
* is re-classified as a fresh handshake would be.
|
|
75
|
+
*/
|
|
76
|
+
export declare function revalidatePrincipal(principal: SocketPrincipal, role: 'agent' | 'browser' | 'stream' | undefined, current: CurrentCredential, nowSeconds: number): string | null;
|
|
22
77
|
//# sourceMappingURL=connectionAuth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectionAuth.d.ts","sourceRoot":"","sources":["../../src/lib/connectionAuth.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"connectionAuth.d.ts","sourceRoot":"","sources":["../../src/lib/connectionAuth.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,UAAU,CAAA;AAClC,eAAO,MAAM,UAAU,SAAS,CAAA;AAIhC,eAAO,MAAM,gBAAgB,8GACgF,CAAA;AAC7G,6FAA6F;AAC7F,eAAO,MAAM,eAAe,sGACyE,CAAA;AACrG,2GAA2G;AAC3G,eAAO,MAAM,qBAAqB,wGACqE,CAAA;AACvG,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,kFACgD,CAAA;AAEjF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,qFAAqF;IACrF,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IAC1B,8FAA8F;IAC9F,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE;AACvC;;;;GAIG;GACD;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAA;CAAE,CAAA;AAS9F,wBAAgB,kBAAkB,CAChC,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,mBAAmB,GACvE,kBAAkB,CAqBpB;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,CAAC,yBAAyB;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AAC9E,uFAAuF;GACrF;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjD,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,UAAU,EAAE,OAAO,CAAA;IACnB,4GAA4G;IAC5G,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CACpD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,eAAe,EAC1B,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,EAChD,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAcf"}
|
|
@@ -1,18 +1,77 @@
|
|
|
1
1
|
export const AGENT_SCOPE = 'agent';
|
|
2
|
+
export const VIEW_SCOPE = 'view';
|
|
3
|
+
const ADMIN_ROLE = 'Admin';
|
|
2
4
|
// ws close reason은 123바이트 한도 (RFC 6455 §5.5.1).
|
|
3
5
|
export const WS_REJECT_REASON = "Unauthorized: agents need a PAT with 'agent' scope (--token / TAPFLOW_AGENT_TOKEN); browsers must sign in";
|
|
6
|
+
/** A PAT with neither `view` nor `agent`: it may upload builds, not open device sessions. */
|
|
7
|
+
export const WS_SCOPE_REASON = "Forbidden: this token lacks the 'view' scope needed for device sessions; create an API-type token";
|
|
8
|
+
/** An `agent` token is issued by an Admin and is only as good as that Admin role, checked at every use. */
|
|
9
|
+
export const WS_AGENT_OWNER_REASON = "Unauthorized: this agent token's owner is no longer an Admin; an Admin must issue a new agent token";
|
|
10
|
+
/** An open socket whose token was revoked, expired, or went with its owner. */
|
|
11
|
+
export const WS_TOKEN_GONE_REASON = 'Unauthorized: this token was revoked or has expired, or its owner was removed';
|
|
4
12
|
// 새 WebSocket 연결을 첫 메시지 도착 전에 분류한다. (주소 × 자격) 매트릭스를
|
|
5
13
|
// 단위 테스트할 수 있도록 순수 함수로 분리 (#271).
|
|
6
|
-
|
|
14
|
+
//
|
|
15
|
+
// **A PAT confers the browser role only with `view`.** Any valid PAT used to, so a `builds:write`
|
|
16
|
+
// token meant for CI uploads could drive devices, and an agent-only token whose first frame was not a
|
|
17
|
+
// handshake fell through to `browser` as well. The HTTP side already asked for `view` (screenshots,
|
|
18
|
+
// UI trees); the socket now asks the same question.
|
|
19
|
+
export function classifyConnection({ isLocal, hasCookieAuth, patScopes, patOwnerRole }) {
|
|
20
|
+
if (isLocal) {
|
|
21
|
+
return hasCookieAuth
|
|
22
|
+
? { action: 'accept', role: 'browser' }
|
|
23
|
+
: { action: 'accept', role: 'first-message', mayBrowse: true, mayRegisterAgent: true };
|
|
24
|
+
}
|
|
25
|
+
const canView = patScopes?.includes(VIEW_SCOPE) ?? false;
|
|
7
26
|
// 명시적 에이전트 자격은 떠돌이 쿠키보다 우선한다 — 원격 에이전트의 인증 경로.
|
|
8
|
-
if (
|
|
9
|
-
|
|
27
|
+
if (patScopes?.includes(AGENT_SCOPE)) {
|
|
28
|
+
if (patOwnerRole === ADMIN_ROLE) {
|
|
29
|
+
return { action: 'accept', role: 'first-message', mayBrowse: canView, mayRegisterAgent: true };
|
|
30
|
+
}
|
|
31
|
+
// A demoted owner's `view,agent` token keeps what `view` alone would give: a Viewer's `view` token
|
|
32
|
+
// may run QA sessions, so taking browsing away here would be stricter than the role model.
|
|
33
|
+
if (canView)
|
|
34
|
+
return { action: 'accept', role: 'browser' };
|
|
35
|
+
return { action: 'reject', reason: WS_AGENT_OWNER_REASON };
|
|
36
|
+
}
|
|
10
37
|
if (hasCookieAuth)
|
|
11
38
|
return { action: 'accept', role: 'browser' };
|
|
12
|
-
if (
|
|
13
|
-
return { action: 'accept', role: 'first-message' };
|
|
14
|
-
if (patScopes)
|
|
39
|
+
if (canView)
|
|
15
40
|
return { action: 'accept', role: 'browser' };
|
|
41
|
+
if (patScopes)
|
|
42
|
+
return { action: 'reject', reason: WS_SCOPE_REASON };
|
|
16
43
|
return { action: 'reject', reason: WS_REJECT_REASON };
|
|
17
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Would this socket still be accepted, given what the database says now? Returns the close reason, or
|
|
47
|
+
* null when it would.
|
|
48
|
+
*
|
|
49
|
+
* **One predicate instead of one rule per event.** Removal, demotion, revocation, expiry and an
|
|
50
|
+
* invitation accept that changes a role all reach an open socket through the same question, asked on
|
|
51
|
+
* the heartbeat and again right after any of those writes. A list of "which sockets does this event
|
|
52
|
+
* affect" is the design this replaced, and it had already missed expiry and the invitation path.
|
|
53
|
+
*
|
|
54
|
+
* `role` is what the socket has become: `agent` and `stream` rest on the `agent` scope and its owner's
|
|
55
|
+
* Admin role; `browser` rests on `view`. A socket that has not sent its first frame yet (`undefined`)
|
|
56
|
+
* is re-classified as a fresh handshake would be.
|
|
57
|
+
*/
|
|
58
|
+
export function revalidatePrincipal(principal, role, current, nowSeconds) {
|
|
59
|
+
if (principal.via === 'cookie') {
|
|
60
|
+
if (!current.userExists)
|
|
61
|
+
return WS_REJECT_REASON;
|
|
62
|
+
if (principal.jwtExp !== undefined && principal.jwtExp <= nowSeconds)
|
|
63
|
+
return WS_REJECT_REASON;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const pat = current.pat;
|
|
67
|
+
if (!pat)
|
|
68
|
+
return WS_TOKEN_GONE_REASON;
|
|
69
|
+
if (role === 'agent' || role === 'stream') {
|
|
70
|
+
return pat.scopes.includes(AGENT_SCOPE) && pat.ownerRole === ADMIN_ROLE ? null : WS_AGENT_OWNER_REASON;
|
|
71
|
+
}
|
|
72
|
+
if (role === 'browser')
|
|
73
|
+
return pat.scopes.includes(VIEW_SCOPE) ? null : WS_SCOPE_REASON;
|
|
74
|
+
const again = classifyConnection({ isLocal: false, hasCookieAuth: false, patScopes: pat.scopes, patOwnerRole: pat.ownerRole });
|
|
75
|
+
return again.action === 'reject' ? again.reason : null;
|
|
76
|
+
}
|
|
18
77
|
//# sourceMappingURL=connectionAuth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectionAuth.js","sourceRoot":"","sources":["../../src/lib/connectionAuth.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"connectionAuth.js","sourceRoot":"","sources":["../../src/lib/connectionAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,13 +7,53 @@ export interface AuthContext {
|
|
|
7
7
|
export declare function signJwt(payload: AuthContext): string;
|
|
8
8
|
export declare function verifyJwtOrThrow(token: string): AuthContext;
|
|
9
9
|
export declare function verifyJwt(token: string): AuthContext | null;
|
|
10
|
-
|
|
10
|
+
/** A signed-in session: who the cookie names, as the users table describes them now. */
|
|
11
|
+
export interface SessionAuth extends AuthContext {
|
|
12
|
+
/** The JWT's `exp` (seconds since the epoch), so an open socket can be closed when it passes. */
|
|
13
|
+
exp?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The cookie's user, **checked against the users table on every call**.
|
|
17
|
+
*
|
|
18
|
+
* The JWT lives seven days and removing a member deletes their row, so a signature check alone kept a
|
|
19
|
+
* removed member signed in for up to a week on every cookie path: this function, `requireViewAuth`,
|
|
20
|
+
* `requireBuildAuth`, recordings, and the WebSocket handshake. This is the one place all of them pass,
|
|
21
|
+
* so the row check lives here. User ids are `AUTOINCREMENT`, so a removed id never comes back and "the
|
|
22
|
+
* row exists" is a sound test. `email` and `role` come from the row, never from the JWT.
|
|
23
|
+
*
|
|
24
|
+
* Only the JWT verification failure becomes `null`. A database error propagates: turning a transient
|
|
25
|
+
* `SQLITE_BUSY` into "not signed in" would sign everyone out and hide the fault.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getAuth(req: http.IncomingMessage): SessionAuth | null;
|
|
28
|
+
export declare function findUser(userId: number): {
|
|
29
|
+
email: string;
|
|
30
|
+
role: string;
|
|
31
|
+
} | null;
|
|
11
32
|
export declare function requireAuth(req: http.IncomingMessage, res: http.ServerResponse): AuthContext | null;
|
|
33
|
+
export declare function currentRole(res: http.ServerResponse, userId: number): string | null;
|
|
12
34
|
export declare function requireRole(req: http.IncomingMessage, res: http.ServerResponse, roles: string[]): AuthContext | null;
|
|
13
|
-
export declare function
|
|
35
|
+
export declare function assertCanWrite(res: http.ServerResponse, userId: number): boolean;
|
|
36
|
+
/** A live PAT and its owner, as the database describes them now. */
|
|
37
|
+
export interface PatAuth {
|
|
38
|
+
patId: number;
|
|
14
39
|
userId: number;
|
|
15
|
-
|
|
16
|
-
|
|
40
|
+
scopes: string[];
|
|
41
|
+
ownerEmail: string;
|
|
42
|
+
ownerRole: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The PAT on this request, or null when there is none, it is unknown, or it has expired.
|
|
46
|
+
*
|
|
47
|
+
* **Reads only.** `last_used_at` is written by `touchPat` once the credential check has accepted the
|
|
48
|
+
* token: a token refused there (a scope it lacks, a WebSocket refused for its scope or its owner's
|
|
49
|
+
* role) is not used, and a "last used" that moved on those told an Admin a leaked token was in use
|
|
50
|
+
* when nothing got through. A route that accepts the token and then refuses the request on the
|
|
51
|
+
* owner's role (`assertCanWrite` for a Viewer) still counts as a use — the token did reach the route.
|
|
52
|
+
*/
|
|
53
|
+
export declare function verifyPat(req: http.IncomingMessage): PatAuth | null;
|
|
54
|
+
/** The same lookup by id, for re-checking a token an open socket was accepted with. */
|
|
55
|
+
export declare function findPat(patId: number): PatAuth | null;
|
|
56
|
+
export declare function touchPat(patId: number): void;
|
|
17
57
|
export declare function hashPat(token: string): string;
|
|
18
58
|
export declare function requireViewAuth(req: http.IncomingMessage, res: http.ServerResponse): AuthContext | null;
|
|
19
59
|
export declare function requireBuildAuth(req: http.IncomingMessage, res: http.ServerResponse): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AAOvB,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb;AAID,wBAAgB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAM3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAM3D;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,WAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AAOvB,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb;AAID,wBAAgB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAM3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAM3D;AAED,wFAAwF;AACxF,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,iGAAiG;IACjG,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,WAAW,GAAG,IAAI,CASrE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAK/E;AAED,wBAAgB,WAAW,CACzB,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,GACvB,WAAW,GAAG,IAAI,CAQpB;AAKD,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUnF;AAED,wBAAgB,WAAW,CACzB,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,KAAK,EAAE,MAAM,EAAE,GACd,WAAW,GAAG,IAAI,CAWpB;AAMD,wBAAgB,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAShF;AAED,oEAAoE;AACpE,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,OAAO,GAAG,IAAI,CAMnE;AAED,uFAAuF;AACvF,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAGrD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,GACvB,WAAW,GAAG,IAAI,CAgBpB;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,GACvB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAc3B"}
|
package/dist/middleware/auth.js
CHANGED
|
@@ -23,12 +23,34 @@ export function verifyJwt(token) {
|
|
|
23
23
|
return null;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* The cookie's user, **checked against the users table on every call**.
|
|
28
|
+
*
|
|
29
|
+
* The JWT lives seven days and removing a member deletes their row, so a signature check alone kept a
|
|
30
|
+
* removed member signed in for up to a week on every cookie path: this function, `requireViewAuth`,
|
|
31
|
+
* `requireBuildAuth`, recordings, and the WebSocket handshake. This is the one place all of them pass,
|
|
32
|
+
* so the row check lives here. User ids are `AUTOINCREMENT`, so a removed id never comes back and "the
|
|
33
|
+
* row exists" is a sound test. `email` and `role` come from the row, never from the JWT.
|
|
34
|
+
*
|
|
35
|
+
* Only the JWT verification failure becomes `null`. A database error propagates: turning a transient
|
|
36
|
+
* `SQLITE_BUSY` into "not signed in" would sign everyone out and hide the fault.
|
|
37
|
+
*/
|
|
26
38
|
export function getAuth(req) {
|
|
27
39
|
const cookie = req.headers.cookie ?? '';
|
|
28
40
|
const match = /(?:^|;\s*)tapflow_token=([^;]+)/.exec(cookie);
|
|
29
41
|
if (!match)
|
|
30
42
|
return null;
|
|
31
|
-
|
|
43
|
+
const claims = verifyJwt(match[1]);
|
|
44
|
+
if (!claims)
|
|
45
|
+
return null;
|
|
46
|
+
const user = findUser(claims.userId);
|
|
47
|
+
if (!user)
|
|
48
|
+
return null;
|
|
49
|
+
return { userId: claims.userId, email: user.email, role: user.role, exp: claims.exp };
|
|
50
|
+
}
|
|
51
|
+
export function findUser(userId) {
|
|
52
|
+
const row = getDb().prepare('SELECT email, role FROM users WHERE id = ?').get(userId);
|
|
53
|
+
return row ?? null;
|
|
32
54
|
}
|
|
33
55
|
export function requireAuth(req, res) {
|
|
34
56
|
const auth = getAuth(req);
|
|
@@ -39,34 +61,90 @@ export function requireAuth(req, res) {
|
|
|
39
61
|
}
|
|
40
62
|
return auth;
|
|
41
63
|
}
|
|
64
|
+
// The cookie JWT carries a role for its whole 7-day life, so a demoted member would keep their old
|
|
65
|
+
// rights for a week and a promoted one would be refused for a week. Every role decision reads the
|
|
66
|
+
// users table instead; the JWT only says who is asking. A missing row means the member was removed.
|
|
67
|
+
export function currentRole(res, userId) {
|
|
68
|
+
const row = getDb().prepare('SELECT role FROM users WHERE id = ?').get(userId);
|
|
69
|
+
if (!row) {
|
|
70
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
71
|
+
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return row.role;
|
|
75
|
+
}
|
|
42
76
|
export function requireRole(req, res, roles) {
|
|
43
77
|
const auth = requireAuth(req, res);
|
|
44
78
|
if (!auth)
|
|
45
79
|
return null;
|
|
46
|
-
|
|
80
|
+
const role = currentRole(res, auth.userId);
|
|
81
|
+
if (role === null)
|
|
82
|
+
return null;
|
|
83
|
+
if (!roles.includes(role)) {
|
|
47
84
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
48
85
|
res.end(JSON.stringify({ error: 'Forbidden' }));
|
|
49
86
|
return null;
|
|
50
87
|
}
|
|
51
|
-
return auth;
|
|
88
|
+
return { ...auth, role };
|
|
52
89
|
}
|
|
90
|
+
// Viewer is read-only: it can look at builds, test them in a QA Session and comment, but not change
|
|
91
|
+
// builds, apps or webhooks. Called after a route's own auth, so it never changes which credentials
|
|
92
|
+
// the route accepts. The PAT path matters most here: any role can mint a builds:write token through
|
|
93
|
+
// the API, so the owner's role is what decides, at the moment the token is used.
|
|
94
|
+
export function assertCanWrite(res, userId) {
|
|
95
|
+
const role = currentRole(res, userId);
|
|
96
|
+
if (role === null)
|
|
97
|
+
return false;
|
|
98
|
+
if (role === 'Viewer') {
|
|
99
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
100
|
+
res.end(JSON.stringify({ error: 'Viewers have read-only access' }));
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
function toPatAuth(row) {
|
|
106
|
+
return {
|
|
107
|
+
patId: row.id,
|
|
108
|
+
userId: row.user_id,
|
|
109
|
+
scopes: row.scope.split(',').map((s) => s.trim()),
|
|
110
|
+
ownerEmail: row.email,
|
|
111
|
+
ownerRole: row.role,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// Joined with users so the owner's current role comes with the token: an `agent` token is only as good
|
|
115
|
+
// as its owner's Admin role, and a Viewer's `builds:write` token is refused on write routes.
|
|
116
|
+
// `datetime(expires_at)`, not the bare column: it is written by `toISOString()` ('2026-09-27T10:00:00.000Z')
|
|
117
|
+
// and `datetime('now')` is '2026-09-27 10:00:00'. Compared as text, 'T' sorts after ' ', so a token that
|
|
118
|
+
// expired this morning kept passing until the end of its UTC day.
|
|
119
|
+
const PAT_SELECT = `
|
|
120
|
+
SELECT pat.id, pat.user_id, pat.scope, u.email, u.role
|
|
121
|
+
FROM personal_access_tokens pat
|
|
122
|
+
JOIN users u ON u.id = pat.user_id
|
|
123
|
+
WHERE (pat.expires_at IS NULL OR datetime(pat.expires_at) > datetime('now'))`;
|
|
124
|
+
/**
|
|
125
|
+
* The PAT on this request, or null when there is none, it is unknown, or it has expired.
|
|
126
|
+
*
|
|
127
|
+
* **Reads only.** `last_used_at` is written by `touchPat` once the credential check has accepted the
|
|
128
|
+
* token: a token refused there (a scope it lacks, a WebSocket refused for its scope or its owner's
|
|
129
|
+
* role) is not used, and a "last used" that moved on those told an Admin a leaked token was in use
|
|
130
|
+
* when nothing got through. A route that accepts the token and then refuses the request on the
|
|
131
|
+
* owner's role (`assertCanWrite` for a Viewer) still counts as a use — the token did reach the route.
|
|
132
|
+
*/
|
|
53
133
|
export function verifyPat(req) {
|
|
54
134
|
const header = req.headers.authorization ?? '';
|
|
55
135
|
if (!header.startsWith('Bearer tflw_pat_'))
|
|
56
136
|
return null;
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
db.prepare(`UPDATE personal_access_tokens SET last_used_at = datetime('now') WHERE token_hash = ?`).run(hash);
|
|
69
|
-
return { userId: row.user_id, scope: row.scope };
|
|
137
|
+
const hash = hashPat(header.slice(7));
|
|
138
|
+
const row = getDb().prepare(`${PAT_SELECT} AND pat.token_hash = ?`).get(hash);
|
|
139
|
+
return row ? toPatAuth(row) : null;
|
|
140
|
+
}
|
|
141
|
+
/** The same lookup by id, for re-checking a token an open socket was accepted with. */
|
|
142
|
+
export function findPat(patId) {
|
|
143
|
+
const row = getDb().prepare(`${PAT_SELECT} AND pat.id = ?`).get(patId);
|
|
144
|
+
return row ? toPatAuth(row) : null;
|
|
145
|
+
}
|
|
146
|
+
export function touchPat(patId) {
|
|
147
|
+
getDb().prepare(`UPDATE personal_access_tokens SET last_used_at = datetime('now') WHERE id = ?`).run(patId);
|
|
70
148
|
}
|
|
71
149
|
export function hashPat(token) {
|
|
72
150
|
return crypto.createHash('sha256').update(token).digest('hex');
|
|
@@ -81,29 +159,23 @@ export function requireViewAuth(req, res) {
|
|
|
81
159
|
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
82
160
|
return null;
|
|
83
161
|
}
|
|
84
|
-
if (!pat.
|
|
162
|
+
if (!pat.scopes.includes('view')) {
|
|
85
163
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
86
164
|
res.end(JSON.stringify({ error: 'Insufficient scope' }));
|
|
87
165
|
return null;
|
|
88
166
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
.get(pat.userId);
|
|
92
|
-
if (!user) {
|
|
93
|
-
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
94
|
-
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
return { userId: pat.userId, email: user.email, role: user.role };
|
|
167
|
+
touchPat(pat.patId);
|
|
168
|
+
return { userId: pat.userId, email: pat.ownerEmail, role: pat.ownerRole };
|
|
98
169
|
}
|
|
99
170
|
export function requireBuildAuth(req, res) {
|
|
100
171
|
const pat = verifyPat(req);
|
|
101
172
|
if (pat !== null) {
|
|
102
|
-
if (!pat.
|
|
173
|
+
if (!pat.scopes.includes('builds:write')) {
|
|
103
174
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
104
175
|
res.end(JSON.stringify({ error: 'Insufficient scope' }));
|
|
105
176
|
return null;
|
|
106
177
|
}
|
|
178
|
+
touchPat(pat.patId);
|
|
107
179
|
return { userId: pat.userId };
|
|
108
180
|
}
|
|
109
181
|
const auth = requireAuth(req, res);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,GAAG,MAAM,cAAc,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAQ/C,MAAM,WAAW,GAAG,IAAI,CAAA;AAExB,MAAM,UAAU,OAAO,CAAC,OAAoB;IAC1C,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAA;AACtE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,CAAgB,CAAA;IACzD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC,+BAA+B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACjE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,CAAC;QACH,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tapflowio/relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "WebSocket relay server for tapflow — NAT traversal, session routing, JWT auth, bundled dashboard",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tapflow",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"nodemailer": "^9.1.1",
|
|
53
53
|
"ws": "^8.21.3",
|
|
54
54
|
"zod": "^4.6.5",
|
|
55
|
-
"@tapflowio/agent-core": "0.
|
|
56
|
-
"@tapflowio/protocol": "0.
|
|
55
|
+
"@tapflowio/agent-core": "0.25.0",
|
|
56
|
+
"@tapflowio/protocol": "0.25.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/better-sqlite3": "^7.6.13",
|