+3
-2
netlify/functions/client-metadata.ts
+3
-2
netlify/functions/client-metadata.ts
···
1
import { Handler, HandlerEvent, HandlerResponse } from "@netlify/functions";
2
3
export const handler: Handler = async (
4
event: HandlerEvent,
···
42
client_name: "ATlast (Local Dev)",
43
client_uri: appUrl,
44
redirect_uris: [redirectUri],
45
-
scope: "atproto repo:app.bsky.graph.follow",
46
grant_types: ["authorization_code", "refresh_token"],
47
response_types: ["code"],
48
application_type: "web",
···
65
client_uri: appUrl,
66
redirect_uris: [redirectUri],
67
logo_uri: logoUri,
68
-
scope: "atproto transition:generic",
69
grant_types: ["authorization_code", "refresh_token"],
70
response_types: ["code"],
71
application_type: "web",
···
1
import { Handler, HandlerEvent, HandlerResponse } from "@netlify/functions";
2
+
import { CONFIG } from "./core/config/constants";
3
4
export const handler: Handler = async (
5
event: HandlerEvent,
···
43
client_name: "ATlast (Local Dev)",
44
client_uri: appUrl,
45
redirect_uris: [redirectUri],
46
+
scope: CONFIG.OAUTH_SCOPES,
47
grant_types: ["authorization_code", "refresh_token"],
48
response_types: ["code"],
49
application_type: "web",
···
66
client_uri: appUrl,
67
redirect_uris: [redirectUri],
68
logo_uri: logoUri,
69
+
scope: CONFIG.OAUTH_SCOPES,
70
grant_types: ["authorization_code", "refresh_token"],
71
response_types: ["code"],
72
application_type: "web",
+3
-1
netlify/functions/core/config/constants.ts
+3
-1
netlify/functions/core/config/constants.ts
···
2
PROFILE_CACHE_TTL: 5 * 60 * 1000, // 5 minutes
3
SESSION_EXPIRY: 30 * 24 * 60 * 60 * 1000, // 30 days
4
STATE_EXPIRY: 10 * 60 * 1000, // 10 minutes
5
+
COOKIE_MAX_AGE: 2592000, // 30 days in seconds,
6
+
OAUTH_KEY_ID: "main-key", // jwks kid
7
+
OAUTH_SCOPES: "atproto transition:generic", // future?: atproto repo:app.bsky.graph.follow?action=create repo:so.sprk.graph.follow?action=create repo:sh.tangled.graph.follow?action=create
8
} as const;
+6
-2
netlify/functions/infrastructure/oauth/OAuthClientFactory.ts
+6
-2
netlify/functions/infrastructure/oauth/OAuthClientFactory.ts
···
6
import { ApiError } from "../../core/errors";
7
import { stateStore, sessionStore } from "./stores";
8
import { getOAuthConfig } from "./config";
9
10
function normalizePrivateKey(key: string): string {
11
if (!key.includes("\n") && key.includes("\\n")) {
···
41
}
42
43
const normalizedKey = normalizePrivateKey(process.env.OAUTH_PRIVATE_KEY);
44
-
const privateKey = await JoseKey.fromImportable(normalizedKey, "main-key");
45
46
return new NodeOAuthClient({
47
clientMetadata: {
···
49
client_name: "ATlast",
50
client_uri: config.clientId.replace("/oauth-client-metadata.json", ""),
51
redirect_uris: [config.redirectUri],
52
-
scope: "atproto transition:generic",
53
grant_types: ["authorization_code", "refresh_token"],
54
response_types: ["code"],
55
application_type: "web",
···
6
import { ApiError } from "../../core/errors";
7
import { stateStore, sessionStore } from "./stores";
8
import { getOAuthConfig } from "./config";
9
+
import { CONFIG } from "../../core/config/constants";
10
11
function normalizePrivateKey(key: string): string {
12
if (!key.includes("\n") && key.includes("\\n")) {
···
42
}
43
44
const normalizedKey = normalizePrivateKey(process.env.OAUTH_PRIVATE_KEY);
45
+
const privateKey = await JoseKey.fromImportable(
46
+
normalizedKey,
47
+
CONFIG.OAUTH_KEY_ID,
48
+
);
49
50
return new NodeOAuthClient({
51
clientMetadata: {
···
53
client_name: "ATlast",
54
client_uri: config.clientId.replace("/oauth-client-metadata.json", ""),
55
redirect_uris: [config.redirectUri],
56
+
scope: CONFIG.OAUTH_SCOPES,
57
grant_types: ["authorization_code", "refresh_token"],
58
response_types: ["code"],
59
application_type: "web",
+2
-1
netlify/functions/infrastructure/oauth/config.ts
+2
-1
netlify/functions/infrastructure/oauth/config.ts
···
1
import { OAuthConfig } from "../../core/types";
2
import { ApiError } from "../../core/errors";
3
import { configCache } from "../cache/CacheService";
4
5
export function getOAuthConfig(event?: {
6
headers: Record<string, string | undefined>;
···
51
"redirect_uri",
52
`http://127.0.0.1:${port}/.netlify/functions/oauth-callback`,
53
],
54
-
["scope", "atproto transition:generic"],
55
])}`;
56
57
console.log("Using loopback OAuth for local development");
···
1
import { OAuthConfig } from "../../core/types";
2
import { ApiError } from "../../core/errors";
3
import { configCache } from "../cache/CacheService";
4
+
import { CONFIG } from "../../core/config/constants";
5
6
export function getOAuthConfig(event?: {
7
headers: Record<string, string | undefined>;
···
52
"redirect_uri",
53
`http://127.0.0.1:${port}/.netlify/functions/oauth-callback`,
54
],
55
+
["scope", CONFIG.OAUTH_SCOPES],
56
])}`;
57
58
console.log("Using loopback OAuth for local development");
+2
-1
netlify/functions/jwks.ts
+2
-1
netlify/functions/jwks.ts
···
1
import { Handler } from "@netlify/functions";
2
+
import { CONFIG } from "./core/config/constants";
3
4
const PUBLIC_JWK = {
5
kty: "EC",
6
x: "3sVbr4xwN7UtmG1L19vL0x9iN-FRcl7p-Wja_xPbhhk",
7
y: "Y1XKDaAyDwijp8aEIGHmO46huKjajSQH2cbfpWaWpQ4",
8
crv: "P-256",
9
+
kid: CONFIG.OAUTH_KEY_ID,
10
use: "sig",
11
alg: "ES256",
12
};
+2
-1
netlify/functions/oauth-start.ts
+2
-1
netlify/functions/oauth-start.ts
···
3
import { successResponse } from "./utils";
4
import { withErrorHandling } from "./core/middleware";
5
import { ValidationError } from "./core/errors";
6
7
interface OAuthStartRequestBody {
8
login_hint?: string;
···
26
const client = await createOAuthClient(event);
27
28
const authUrl = await client.authorize(loginHint, {
29
-
scope: "atproto transition:generic",
30
});
31
32
console.log("[oauth-start] Generated auth URL for:", loginHint);
···
3
import { successResponse } from "./utils";
4
import { withErrorHandling } from "./core/middleware";
5
import { ValidationError } from "./core/errors";
6
+
import { CONFIG } from "./core/config/constants";
7
8
interface OAuthStartRequestBody {
9
login_hint?: string;
···
27
const client = await createOAuthClient(event);
28
29
const authUrl = await client.authorize(loginHint, {
30
+
scope: CONFIG.OAUTH_SCOPES,
31
});
32
33
console.log("[oauth-start] Generated auth URL for:", loginHint);