@atria/server 0.0.9 → 0.0.12
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 +26 -10
- package/dist/auth/cookies.d.ts +11 -0
- package/dist/auth/cookies.js +38 -0
- package/dist/auth/cookies.js.map +1 -0
- package/dist/auth/hash.d.ts +2 -0
- package/dist/auth/hash.js +36 -0
- package/dist/auth/hash.js.map +1 -0
- package/dist/auth/oauth.d.ts +5 -0
- package/dist/auth/oauth.js +215 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/runtime.d.ts +28 -0
- package/dist/auth/runtime.js +528 -0
- package/dist/auth/runtime.js.map +1 -0
- package/dist/auth/store.d.ts +33 -0
- package/dist/auth/store.js +49 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/auth/types.d.ts +40 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/validation.d.ts +21 -0
- package/dist/auth/validation.js +97 -0
- package/dist/auth/validation.js.map +1 -0
- package/dist/dev/admin/assets.d.ts +2 -0
- package/dist/dev/admin/assets.js +30 -0
- package/dist/dev/admin/assets.js.map +1 -0
- package/dist/dev/admin/i18n.d.ts +2 -0
- package/dist/dev/admin/i18n.js +63 -0
- package/dist/dev/admin/i18n.js.map +1 -0
- package/dist/dev/admin/index.d.ts +3 -0
- package/dist/dev/admin/index.js +4 -0
- package/dist/dev/admin/index.js.map +1 -0
- package/dist/dev/admin/request.d.ts +14 -0
- package/dist/dev/admin/request.js +77 -0
- package/dist/dev/admin/request.js.map +1 -0
- package/dist/dev/admin/routing.d.ts +5 -0
- package/dist/dev/admin/routing.js +31 -0
- package/dist/dev/admin/routing.js.map +1 -0
- package/dist/dev/admin-assets.d.ts +2 -0
- package/dist/dev/admin-assets.js +30 -0
- package/dist/dev/admin-assets.js.map +1 -0
- package/dist/dev/admin.d.ts +2 -0
- package/dist/dev/admin.js +30 -0
- package/dist/dev/admin.js.map +1 -0
- package/dist/dev/constants.d.ts +11 -0
- package/dist/dev/constants.js +22 -0
- package/dist/dev/constants.js.map +1 -0
- package/dist/dev/health/index.d.ts +2 -0
- package/dist/dev/health/index.js +3 -0
- package/dist/dev/health/index.js.map +1 -0
- package/dist/dev/health/request.d.ts +14 -0
- package/dist/dev/health/request.js +22 -0
- package/dist/dev/health/request.js.map +1 -0
- package/dist/dev/health/state.d.ts +9 -0
- package/dist/dev/health/state.js +52 -0
- package/dist/dev/health/state.js.map +1 -0
- package/dist/dev/health.d.ts +9 -0
- package/dist/dev/health.js +52 -0
- package/dist/dev/health.js.map +1 -0
- package/dist/dev/http/errors.d.ts +2 -0
- package/dist/dev/http/errors.js +6 -0
- package/dist/dev/http/errors.js.map +1 -0
- package/dist/dev/http/routing.d.ts +3 -0
- package/dist/dev/http/routing.js +26 -0
- package/dist/dev/http/routing.js.map +1 -0
- package/dist/dev/i18n.d.ts +2 -0
- package/dist/dev/i18n.js +63 -0
- package/dist/dev/i18n.js.map +1 -0
- package/dist/dev/lifecycle.d.ts +2 -0
- package/dist/dev/lifecycle.js +10 -0
- package/dist/dev/lifecycle.js.map +1 -0
- package/dist/dev/public/index.d.ts +1 -0
- package/dist/dev/public/index.js +2 -0
- package/dist/dev/public/index.js.map +1 -0
- package/dist/dev/public/request.d.ts +9 -0
- package/dist/dev/public/request.js +29 -0
- package/dist/dev/public/request.js.map +1 -0
- package/dist/dev/public/responses.d.ts +3 -0
- package/dist/dev/public/responses.js +25 -0
- package/dist/dev/public/responses.js.map +1 -0
- package/dist/dev/public/routing.d.ts +1 -0
- package/dist/dev/public/routing.js +5 -0
- package/dist/dev/public/routing.js.map +1 -0
- package/dist/dev/responses.d.ts +2 -0
- package/dist/dev/responses.js +3 -0
- package/dist/dev/responses.js.map +1 -0
- package/dist/dev/routing.d.ts +3 -0
- package/dist/dev/routing.js +4 -0
- package/dist/dev/routing.js.map +1 -0
- package/dist/dev/setup/index.d.ts +2 -0
- package/dist/dev/setup/index.js +2 -0
- package/dist/dev/setup/index.js.map +1 -0
- package/dist/dev/setup/request.d.ts +5 -0
- package/dist/dev/setup/request.js +20 -0
- package/dist/dev/setup/request.js.map +1 -0
- package/dist/dev/setup/types.d.ts +5 -0
- package/dist/dev/setup/types.js +2 -0
- package/dist/dev/setup/types.js.map +1 -0
- package/dist/dev/static/files.d.ts +6 -0
- package/dist/dev/static/files.js +74 -0
- package/dist/dev/static/files.js.map +1 -0
- package/dist/dev/static/index.d.ts +4 -0
- package/dist/dev/static/index.js +5 -0
- package/dist/dev/static/index.js.map +1 -0
- package/dist/dev/static/paths.d.ts +1 -0
- package/dist/dev/static/paths.js +6 -0
- package/dist/dev/static/paths.js.map +1 -0
- package/dist/dev/static/publish.d.ts +1 -0
- package/dist/dev/static/publish.js +13 -0
- package/dist/dev/static/publish.js.map +1 -0
- package/dist/dev/static/resolver.d.ts +2 -0
- package/dist/dev/static/resolver.js +53 -0
- package/dist/dev/static/resolver.js.map +1 -0
- package/dist/dev/static/sender.d.ts +2 -0
- package/dist/dev/static/sender.js +11 -0
- package/dist/dev/static/sender.js.map +1 -0
- package/dist/dev/static-files.d.ts +6 -0
- package/dist/dev/static-files.js +74 -0
- package/dist/dev/static-files.js.map +1 -0
- package/dist/dev/types.d.ts +10 -0
- package/dist/dev/types.js +2 -0
- package/dist/dev/types.js.map +1 -0
- package/dist/server.js +65 -190
- package/dist/server.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
HTTP runtime server for atria development.
|
|
4
4
|
|
|
5
|
-
This package powers `atria dev` by serving `public/` for the public site and `.atria/runtime` for the back-office host.
|
|
5
|
+
This package powers `atria dev` by serving `production/public/` for the public site and `.atria/runtime` for the back-office host.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @atria/server @atria/shared
|
|
10
|
+
npm install @atria/server @atria/shared @atria/db
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
@@ -27,12 +27,28 @@ await server.close();
|
|
|
27
27
|
|
|
28
28
|
## Behavior
|
|
29
29
|
|
|
30
|
-
- Serves `localhost` from `<projectRoot>/public`.
|
|
31
|
-
- Serves `
|
|
32
|
-
- Responds to `GET /api/health` with
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
30
|
+
- Serves `localhost` from `<projectRoot>/production/public`.
|
|
31
|
+
- Serves `studio.localhost` from `<projectRoot>/.atria/runtime`.
|
|
32
|
+
- Responds to `GET /api/health` with runtime diagnostics: `ok`, `status`, `site`, `publicOutputPublished`, `ownerSetupPending`, and `database` (driver/source/usesFallback/reachable/error).
|
|
33
|
+
- Exposes OAuth auth endpoints on `studio.localhost`:
|
|
34
|
+
- `GET /api/auth/providers`
|
|
35
|
+
- `GET /api/auth/start/:provider`
|
|
36
|
+
- `GET /api/auth/callback/:provider`
|
|
37
|
+
- `GET /api/auth/session`
|
|
38
|
+
- `GET /api/auth/logout`
|
|
39
|
+
- Publish state is resolved live from `<projectRoot>/production/public/index.html` (no marker file required).
|
|
40
|
+
- Owner setup state is resolved from the database (`pending` while no owner user exists).
|
|
41
|
+
- If `production/public/index.html` is missing, `GET /` returns `503 Service Unavailable`.
|
|
42
|
+
- If `production/public/index.html` is missing, other public routes return `404 Not Found`.
|
|
43
|
+
- If `production/public/index.html` exists, missing public files/routes return `404` (serving `production/public/404.html` when present).
|
|
44
|
+
- Uses SPA-style `index.html` fallback only for `studio.localhost` non-file routes.
|
|
38
45
|
- Blocks path traversal attempts outside the configured public/admin roots.
|
|
46
|
+
|
|
47
|
+
## OAuth environment variables
|
|
48
|
+
|
|
49
|
+
- `ATRIA_AUTH_BROKER_ORIGIN` (recommended)
|
|
50
|
+
- `ATRIA_AUTH_GITHUB_CLIENT_ID`
|
|
51
|
+
- `ATRIA_AUTH_GITHUB_CLIENT_SECRET`
|
|
52
|
+
- `ATRIA_AUTH_GOOGLE_CLIENT_ID`
|
|
53
|
+
- `ATRIA_AUTH_GOOGLE_CLIENT_SECRET`
|
|
54
|
+
- `ATRIA_AUTH_ORIGIN` (optional, defaults to `http://studio.localhost:<port>`)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from "node:http";
|
|
2
|
+
interface CookieOptions {
|
|
3
|
+
httpOnly?: boolean;
|
|
4
|
+
maxAge?: number;
|
|
5
|
+
path?: string;
|
|
6
|
+
sameSite?: "Lax" | "Strict" | "None";
|
|
7
|
+
secure?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const parseCookies: (headers: IncomingHttpHeaders) => Record<string, string>;
|
|
10
|
+
export declare const serializeCookie: (name: string, value: string, options?: CookieOptions) => string;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const parseCookies = (headers) => {
|
|
2
|
+
const raw = headers.cookie;
|
|
3
|
+
if (!raw) {
|
|
4
|
+
return {};
|
|
5
|
+
}
|
|
6
|
+
return raw
|
|
7
|
+
.split(";")
|
|
8
|
+
.map((entry) => entry.trim())
|
|
9
|
+
.filter((entry) => entry.length > 0)
|
|
10
|
+
.reduce((cookies, entry) => {
|
|
11
|
+
const separatorIndex = entry.indexOf("=");
|
|
12
|
+
if (separatorIndex <= 0) {
|
|
13
|
+
return cookies;
|
|
14
|
+
}
|
|
15
|
+
const name = entry.slice(0, separatorIndex).trim();
|
|
16
|
+
const value = entry.slice(separatorIndex + 1).trim();
|
|
17
|
+
cookies[name] = decodeURIComponent(value);
|
|
18
|
+
return cookies;
|
|
19
|
+
}, {});
|
|
20
|
+
};
|
|
21
|
+
export const serializeCookie = (name, value, options = {}) => {
|
|
22
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
23
|
+
parts.push(`Path=${options.path ?? "/"}`);
|
|
24
|
+
if (options.httpOnly !== false) {
|
|
25
|
+
parts.push("HttpOnly");
|
|
26
|
+
}
|
|
27
|
+
if (options.sameSite) {
|
|
28
|
+
parts.push(`SameSite=${options.sameSite}`);
|
|
29
|
+
}
|
|
30
|
+
if (typeof options.maxAge === "number") {
|
|
31
|
+
parts.push(`Max-Age=${Math.max(0, Math.floor(options.maxAge))}`);
|
|
32
|
+
}
|
|
33
|
+
if (options.secure) {
|
|
34
|
+
parts.push("Secure");
|
|
35
|
+
}
|
|
36
|
+
return parts.join("; ");
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/auth/cookies.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAA4B,EAA0B,EAAE;IACnF,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;SACnC,MAAM,CAAyB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACjD,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC1C,OAAO,OAAO,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAAY,EACZ,KAAa,EACb,UAAyB,EAAE,EACnB,EAAE;IACV,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAEvD,KAAK,CAAC,IAAI,CAAC,QAAQ,OAAO,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;IAE1C,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzB,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,YAAY,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { randomBytes, scrypt as scryptCallback, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const scrypt = promisify(scryptCallback);
|
|
4
|
+
const HASH_SCHEME = "scrypt";
|
|
5
|
+
const SALT_BYTES = 16;
|
|
6
|
+
const KEY_BYTES = 64;
|
|
7
|
+
const toBuffer = async (secret, salt) => Buffer.from(await scrypt(secret, salt, KEY_BYTES));
|
|
8
|
+
export const hashSecret = async (secret) => {
|
|
9
|
+
const salt = randomBytes(SALT_BYTES);
|
|
10
|
+
const derived = await toBuffer(secret, salt);
|
|
11
|
+
return `${HASH_SCHEME}$${salt.toString("hex")}$${derived.toString("hex")}`;
|
|
12
|
+
};
|
|
13
|
+
export const verifySecret = async (secret, storedHash) => {
|
|
14
|
+
const parts = storedHash.split("$");
|
|
15
|
+
if (parts.length !== 3) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const [scheme, saltHex, hashHex] = parts;
|
|
19
|
+
if (scheme !== HASH_SCHEME) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (!saltHex || !hashHex) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const salt = Buffer.from(saltHex, "hex");
|
|
26
|
+
const expectedHash = Buffer.from(hashHex, "hex");
|
|
27
|
+
if (salt.length === 0 || expectedHash.length === 0) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const actualHash = await toBuffer(secret, salt);
|
|
31
|
+
if (actualHash.length !== expectedHash.length) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return timingSafeEqual(actualHash, expectedHash);
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/auth/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,IAAI,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,MAAM,GAAG,SAAS,CAAC,cAAc,CAInB,CAAC;AAErB,MAAM,WAAW,GAAG,QAAQ,CAAC;AAC7B,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAE,IAAY,EAAmB,EAAE,CACvE,MAAM,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;AAErD,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAc,EAAmB,EAAE;IAClE,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7C,OAAO,GAAG,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AAC7E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,MAAc,EAAE,UAAkB,EAAoB,EAAE;IACzF,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;IACzC,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACjD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,IAAI,UAAU,CAAC,MAAM,KAAK,YAAY,CAAC,MAAM,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,eAAe,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AACnD,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OAuthProfile, OAuthProviderId } from "./types.js";
|
|
2
|
+
export declare const listConfiguredOAuthProviders: () => OAuthProviderId[];
|
|
3
|
+
export declare const buildOAuthAuthorizationUrl: (provider: OAuthProviderId, callbackUrl: string, stateId: string) => string;
|
|
4
|
+
export declare const getOAuthProfileFromCode: (provider: OAuthProviderId, code: string, callbackUrl: string) => Promise<OAuthProfile>;
|
|
5
|
+
export declare const createOAuthStateId: () => string;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
const PROVIDERS = {
|
|
3
|
+
github: {
|
|
4
|
+
id: "github",
|
|
5
|
+
authorizationEndpoint: "https://github.com/login/oauth/authorize",
|
|
6
|
+
tokenEndpoint: "https://github.com/login/oauth/access_token",
|
|
7
|
+
scopes: ["read:user", "user:email"]
|
|
8
|
+
},
|
|
9
|
+
google: {
|
|
10
|
+
id: "google",
|
|
11
|
+
authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
12
|
+
tokenEndpoint: "https://oauth2.googleapis.com/token",
|
|
13
|
+
scopes: ["openid", "profile", "email"]
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const hasProviderConfig = (provider) => {
|
|
17
|
+
if (provider === "github") {
|
|
18
|
+
return Boolean(process.env.ATRIA_AUTH_GITHUB_CLIENT_ID && process.env.ATRIA_AUTH_GITHUB_CLIENT_SECRET);
|
|
19
|
+
}
|
|
20
|
+
return Boolean(process.env.ATRIA_AUTH_GOOGLE_CLIENT_ID && process.env.ATRIA_AUTH_GOOGLE_CLIENT_SECRET);
|
|
21
|
+
};
|
|
22
|
+
const getOAuthClientConfig = (provider, callbackUrl) => {
|
|
23
|
+
if (provider === "github") {
|
|
24
|
+
const clientId = process.env.ATRIA_AUTH_GITHUB_CLIENT_ID;
|
|
25
|
+
const clientSecret = process.env.ATRIA_AUTH_GITHUB_CLIENT_SECRET;
|
|
26
|
+
if (!clientId || !clientSecret) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return { clientId, clientSecret, callbackUrl };
|
|
30
|
+
}
|
|
31
|
+
const clientId = process.env.ATRIA_AUTH_GOOGLE_CLIENT_ID;
|
|
32
|
+
const clientSecret = process.env.ATRIA_AUTH_GOOGLE_CLIENT_SECRET;
|
|
33
|
+
if (!clientId || !clientSecret) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return { clientId, clientSecret, callbackUrl };
|
|
37
|
+
};
|
|
38
|
+
const toJson = async (response) => {
|
|
39
|
+
const text = await response.text();
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(text);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new Error(`Invalid JSON response from OAuth provider: ${text}`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const exchangeCodeForAccessToken = async (provider, code, callbackUrl) => {
|
|
48
|
+
const providerConfig = PROVIDERS[provider];
|
|
49
|
+
const clientConfig = getOAuthClientConfig(provider, callbackUrl);
|
|
50
|
+
if (!providerConfig || !clientConfig) {
|
|
51
|
+
throw new Error(`OAuth provider "${provider}" is not configured.`);
|
|
52
|
+
}
|
|
53
|
+
const body = new URLSearchParams({
|
|
54
|
+
client_id: clientConfig.clientId,
|
|
55
|
+
client_secret: clientConfig.clientSecret,
|
|
56
|
+
code,
|
|
57
|
+
redirect_uri: clientConfig.callbackUrl,
|
|
58
|
+
grant_type: "authorization_code"
|
|
59
|
+
});
|
|
60
|
+
const tokenResponse = await fetch(providerConfig.tokenEndpoint, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: {
|
|
63
|
+
accept: "application/json",
|
|
64
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
65
|
+
},
|
|
66
|
+
body
|
|
67
|
+
});
|
|
68
|
+
const tokenPayload = await toJson(tokenResponse);
|
|
69
|
+
const accessToken = typeof tokenPayload.access_token === "string" ? tokenPayload.access_token : null;
|
|
70
|
+
if (!tokenResponse.ok || !accessToken) {
|
|
71
|
+
const errorDescription = typeof tokenPayload.error_description === "string"
|
|
72
|
+
? tokenPayload.error_description
|
|
73
|
+
: typeof tokenPayload.error === "string"
|
|
74
|
+
? tokenPayload.error
|
|
75
|
+
: tokenResponse.statusText;
|
|
76
|
+
throw new Error(`OAuth token exchange failed (${provider}): ${errorDescription}`);
|
|
77
|
+
}
|
|
78
|
+
return { accessToken };
|
|
79
|
+
};
|
|
80
|
+
const toGitHubEmailEntry = (value) => typeof value === "object" && value !== null ? value : null;
|
|
81
|
+
const getGitHubEmailValue = (entry) => entry && typeof entry.email === "string" ? entry.email : null;
|
|
82
|
+
const isGitHubEmailVerified = (entry) => Boolean(entry && entry.verified === true && typeof entry.email === "string");
|
|
83
|
+
const isGitHubEmailPrimaryAndVerified = (entry) => Boolean(entry &&
|
|
84
|
+
entry.primary === true &&
|
|
85
|
+
entry.verified === true &&
|
|
86
|
+
typeof entry.email === "string");
|
|
87
|
+
const fetchGitHubProfile = async (accessToken) => {
|
|
88
|
+
const profileResponse = await fetch("https://api.github.com/user", {
|
|
89
|
+
headers: {
|
|
90
|
+
authorization: `Bearer ${accessToken}`,
|
|
91
|
+
"user-agent": "atria-dev-server",
|
|
92
|
+
accept: "application/vnd.github+json"
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const profilePayload = await toJson(profileResponse);
|
|
96
|
+
if (!profileResponse.ok) {
|
|
97
|
+
throw new Error(`GitHub profile request failed: ${profileResponse.statusText}`);
|
|
98
|
+
}
|
|
99
|
+
const githubUserId = typeof profilePayload.id === "number"
|
|
100
|
+
? String(profilePayload.id)
|
|
101
|
+
: typeof profilePayload.id === "string"
|
|
102
|
+
? profilePayload.id
|
|
103
|
+
: null;
|
|
104
|
+
if (!githubUserId) {
|
|
105
|
+
throw new Error("GitHub profile did not return a valid user id.");
|
|
106
|
+
}
|
|
107
|
+
let email = typeof profilePayload.email === "string" ? profilePayload.email : null;
|
|
108
|
+
let emailVerified = false;
|
|
109
|
+
const emailResponse = await fetch("https://api.github.com/user/emails", {
|
|
110
|
+
headers: {
|
|
111
|
+
authorization: `Bearer ${accessToken}`,
|
|
112
|
+
"user-agent": "atria-dev-server",
|
|
113
|
+
accept: "application/vnd.github+json"
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
if (emailResponse.ok) {
|
|
117
|
+
const emailPayload = (await toJson(emailResponse));
|
|
118
|
+
if (Array.isArray(emailPayload)) {
|
|
119
|
+
const entries = emailPayload
|
|
120
|
+
.map((entry) => toGitHubEmailEntry(entry))
|
|
121
|
+
.filter((entry) => entry !== null);
|
|
122
|
+
const verifiedPrimary = entries.find((entry) => isGitHubEmailPrimaryAndVerified(entry)) ?? null;
|
|
123
|
+
const verifiedAny = entries.find((entry) => isGitHubEmailVerified(entry)) ?? null;
|
|
124
|
+
const firstAny = entries.find((entry) => typeof entry.email === "string") ?? null;
|
|
125
|
+
const verifiedPrimaryEmail = getGitHubEmailValue(verifiedPrimary);
|
|
126
|
+
const verifiedAnyEmail = getGitHubEmailValue(verifiedAny);
|
|
127
|
+
const firstAnyEmail = getGitHubEmailValue(firstAny);
|
|
128
|
+
if (verifiedPrimaryEmail) {
|
|
129
|
+
email = verifiedPrimaryEmail;
|
|
130
|
+
emailVerified = true;
|
|
131
|
+
}
|
|
132
|
+
else if (verifiedAnyEmail) {
|
|
133
|
+
email = verifiedAnyEmail;
|
|
134
|
+
emailVerified = true;
|
|
135
|
+
}
|
|
136
|
+
else if (!email && firstAnyEmail) {
|
|
137
|
+
email = firstAnyEmail;
|
|
138
|
+
}
|
|
139
|
+
if (email && !emailVerified) {
|
|
140
|
+
const normalizedEmail = email.toLowerCase();
|
|
141
|
+
const matchingVerified = entries.find((entry) => {
|
|
142
|
+
if (!isGitHubEmailVerified(entry)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
const entryEmail = getGitHubEmailValue(entry);
|
|
146
|
+
return entryEmail ? entryEmail.toLowerCase() === normalizedEmail : false;
|
|
147
|
+
});
|
|
148
|
+
emailVerified = matchingVerified !== undefined;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
provider: "github",
|
|
154
|
+
providerUserId: githubUserId,
|
|
155
|
+
email,
|
|
156
|
+
emailVerified,
|
|
157
|
+
name: typeof profilePayload.name === "string" ? profilePayload.name : null,
|
|
158
|
+
avatarUrl: typeof profilePayload.avatar_url === "string" ? profilePayload.avatar_url : null
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
const fetchGoogleProfile = async (accessToken) => {
|
|
162
|
+
const profileResponse = await fetch("https://openidconnect.googleapis.com/v1/userinfo", {
|
|
163
|
+
headers: {
|
|
164
|
+
authorization: `Bearer ${accessToken}`,
|
|
165
|
+
accept: "application/json"
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
const profilePayload = await toJson(profileResponse);
|
|
169
|
+
if (!profileResponse.ok) {
|
|
170
|
+
throw new Error(`Google profile request failed: ${profileResponse.statusText}`);
|
|
171
|
+
}
|
|
172
|
+
const googleUserId = typeof profilePayload.sub === "string" ? profilePayload.sub : null;
|
|
173
|
+
if (!googleUserId) {
|
|
174
|
+
throw new Error("Google profile did not return a valid user id.");
|
|
175
|
+
}
|
|
176
|
+
const email = typeof profilePayload.email === "string" ? profilePayload.email : null;
|
|
177
|
+
const emailVerified = email !== null && profilePayload.email_verified === true;
|
|
178
|
+
return {
|
|
179
|
+
provider: "google",
|
|
180
|
+
providerUserId: googleUserId,
|
|
181
|
+
email,
|
|
182
|
+
emailVerified,
|
|
183
|
+
name: typeof profilePayload.name === "string" ? profilePayload.name : null,
|
|
184
|
+
avatarUrl: typeof profilePayload.picture === "string" ? profilePayload.picture : null
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
export const listConfiguredOAuthProviders = () => Object.keys(PROVIDERS).filter((provider) => hasProviderConfig(provider));
|
|
188
|
+
export const buildOAuthAuthorizationUrl = (provider, callbackUrl, stateId) => {
|
|
189
|
+
const providerConfig = PROVIDERS[provider];
|
|
190
|
+
const clientConfig = getOAuthClientConfig(provider, callbackUrl);
|
|
191
|
+
if (!providerConfig || !clientConfig) {
|
|
192
|
+
throw new Error(`OAuth provider "${provider}" is not configured.`);
|
|
193
|
+
}
|
|
194
|
+
const query = new URLSearchParams({
|
|
195
|
+
client_id: clientConfig.clientId,
|
|
196
|
+
redirect_uri: clientConfig.callbackUrl,
|
|
197
|
+
response_type: "code",
|
|
198
|
+
scope: providerConfig.scopes.join(" "),
|
|
199
|
+
state: stateId
|
|
200
|
+
});
|
|
201
|
+
if (provider === "google") {
|
|
202
|
+
query.set("access_type", "offline");
|
|
203
|
+
query.set("prompt", "consent");
|
|
204
|
+
}
|
|
205
|
+
return `${providerConfig.authorizationEndpoint}?${query.toString()}`;
|
|
206
|
+
};
|
|
207
|
+
export const getOAuthProfileFromCode = async (provider, code, callbackUrl) => {
|
|
208
|
+
const token = await exchangeCodeForAccessToken(provider, code, callbackUrl);
|
|
209
|
+
if (provider === "github") {
|
|
210
|
+
return fetchGitHubProfile(token.accessToken);
|
|
211
|
+
}
|
|
212
|
+
return fetchGoogleProfile(token.accessToken);
|
|
213
|
+
};
|
|
214
|
+
export const createOAuthStateId = () => randomBytes(24).toString("hex");
|
|
215
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { type AuthMethod } from "@atria/shared";
|
|
3
|
+
interface CreateAuthRuntimeOptions {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
port: number;
|
|
6
|
+
}
|
|
7
|
+
interface OwnerSetupState {
|
|
8
|
+
pending: boolean;
|
|
9
|
+
preferredAuthMethod: AuthMethod | null;
|
|
10
|
+
}
|
|
11
|
+
export interface AuthRuntime {
|
|
12
|
+
handleRequest: (request: IncomingMessage, response: ServerResponse, requestUrl: URL) => Promise<boolean>;
|
|
13
|
+
hasUsers: () => Promise<boolean>;
|
|
14
|
+
getOwnerSetupState: () => Promise<OwnerSetupState>;
|
|
15
|
+
getSession: (request: IncomingMessage) => Promise<SessionResult>;
|
|
16
|
+
close: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
interface SessionResult {
|
|
19
|
+
authenticated: boolean;
|
|
20
|
+
user: {
|
|
21
|
+
id: string;
|
|
22
|
+
email: string | null;
|
|
23
|
+
name: string | null;
|
|
24
|
+
avatarUrl: string | null;
|
|
25
|
+
} | null;
|
|
26
|
+
}
|
|
27
|
+
export declare const createAuthRuntime: (options: CreateAuthRuntimeOptions) => AuthRuntime;
|
|
28
|
+
export {};
|