@commonpub/server 0.5.0 → 0.7.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.
Files changed (43) hide show
  1. package/dist/federation/delivery.d.ts.map +1 -1
  2. package/dist/federation/delivery.js +160 -21
  3. package/dist/federation/delivery.js.map +1 -1
  4. package/dist/federation/federation.d.ts +41 -0
  5. package/dist/federation/federation.d.ts.map +1 -1
  6. package/dist/federation/federation.js +248 -20
  7. package/dist/federation/federation.js.map +1 -1
  8. package/dist/federation/hubFederation.d.ts +38 -0
  9. package/dist/federation/hubFederation.d.ts.map +1 -0
  10. package/dist/federation/hubFederation.js +212 -0
  11. package/dist/federation/hubFederation.js.map +1 -0
  12. package/dist/federation/inboxHandlers.d.ts.map +1 -1
  13. package/dist/federation/inboxHandlers.js +261 -52
  14. package/dist/federation/inboxHandlers.js.map +1 -1
  15. package/dist/federation/index.d.ts +5 -1
  16. package/dist/federation/index.d.ts.map +1 -1
  17. package/dist/federation/index.js +5 -1
  18. package/dist/federation/index.js.map +1 -1
  19. package/dist/federation/mirroring.d.ts +65 -0
  20. package/dist/federation/mirroring.d.ts.map +1 -0
  21. package/dist/federation/mirroring.js +172 -0
  22. package/dist/federation/mirroring.js.map +1 -0
  23. package/dist/federation/oauth.d.ts +115 -0
  24. package/dist/federation/oauth.d.ts.map +1 -0
  25. package/dist/federation/oauth.js +277 -0
  26. package/dist/federation/oauth.js.map +1 -0
  27. package/dist/federation/timeline.d.ts +89 -0
  28. package/dist/federation/timeline.d.ts.map +1 -0
  29. package/dist/federation/timeline.js +344 -0
  30. package/dist/federation/timeline.js.map +1 -0
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/social/index.d.ts +1 -1
  36. package/dist/social/index.d.ts.map +1 -1
  37. package/dist/social/index.js +1 -1
  38. package/dist/social/index.js.map +1 -1
  39. package/dist/social/social.d.ts +1 -0
  40. package/dist/social/social.d.ts.map +1 -1
  41. package/dist/social/social.js +8 -1
  42. package/dist/social/social.js.map +1 -1
  43. package/package.json +6 -6
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Instance mirroring — subscribe to all content from a remote instance.
3
+ * Uses standard AP semantics: instance B's Service actor follows instance A's Service actor.
4
+ * Content arrives via normal inbox Create activities, filtered by mirror config.
5
+ */
6
+ import { eq, and, sql } from 'drizzle-orm';
7
+ import { instanceMirrors, activities, followRelationships, } from '@commonpub/schema';
8
+ import { buildFollowActivity } from '@commonpub/protocol';
9
+ /**
10
+ * Create a new instance mirror subscription.
11
+ * The instance actor will follow the remote instance actor to receive content.
12
+ */
13
+ export async function createMirror(db, remoteDomain, remoteActorUri, direction, localDomain, filters) {
14
+ const [row] = await db
15
+ .insert(instanceMirrors)
16
+ .values({
17
+ remoteDomain,
18
+ remoteActorUri,
19
+ direction,
20
+ status: 'active', // Start as active immediately
21
+ filterContentTypes: filters?.contentTypes ?? null,
22
+ filterTags: filters?.tags ?? null,
23
+ })
24
+ .returning();
25
+ // For pull mirrors: Follow the remote instance actor so they deliver content to us
26
+ if (direction === 'pull') {
27
+ const localActorUri = `https://${localDomain}/actor`;
28
+ const followActivity = buildFollowActivity(localDomain, localActorUri, remoteActorUri);
29
+ // Store the follow relationship so the remote can find us
30
+ await db.insert(followRelationships).values({
31
+ followerActorUri: localActorUri,
32
+ followingActorUri: remoteActorUri,
33
+ status: 'pending',
34
+ }).onConflictDoNothing();
35
+ // Queue the Follow for delivery
36
+ await db.insert(activities).values({
37
+ type: 'Follow',
38
+ actorUri: localActorUri,
39
+ objectUri: remoteActorUri,
40
+ payload: followActivity,
41
+ direction: 'outbound',
42
+ status: 'pending',
43
+ });
44
+ }
45
+ return mirrorRowToConfig(row);
46
+ }
47
+ /**
48
+ * Activate a pending mirror (called after the Follow is accepted).
49
+ */
50
+ export async function activateMirror(db, mirrorId) {
51
+ await db
52
+ .update(instanceMirrors)
53
+ .set({ status: 'active', updatedAt: new Date() })
54
+ .where(eq(instanceMirrors.id, mirrorId));
55
+ }
56
+ /**
57
+ * Pause a mirror — stops content ingestion but keeps the follow active.
58
+ */
59
+ export async function pauseMirror(db, mirrorId) {
60
+ await db
61
+ .update(instanceMirrors)
62
+ .set({ status: 'paused', updatedAt: new Date() })
63
+ .where(eq(instanceMirrors.id, mirrorId));
64
+ }
65
+ /**
66
+ * Resume a paused mirror.
67
+ */
68
+ export async function resumeMirror(db, mirrorId) {
69
+ await db
70
+ .update(instanceMirrors)
71
+ .set({ status: 'active', updatedAt: new Date() })
72
+ .where(eq(instanceMirrors.id, mirrorId));
73
+ }
74
+ /**
75
+ * Cancel a mirror — removes the subscription entirely.
76
+ * Content already received is NOT deleted (can be cleaned up separately).
77
+ */
78
+ export async function cancelMirror(db, mirrorId) {
79
+ await db.delete(instanceMirrors).where(eq(instanceMirrors.id, mirrorId));
80
+ }
81
+ /**
82
+ * List all instance mirrors with stats.
83
+ */
84
+ export async function listMirrors(db) {
85
+ const rows = await db.select().from(instanceMirrors).orderBy(instanceMirrors.createdAt);
86
+ return rows.map(mirrorRowToConfig);
87
+ }
88
+ /**
89
+ * Get a single mirror by ID.
90
+ */
91
+ export async function getMirror(db, mirrorId) {
92
+ const [row] = await db
93
+ .select()
94
+ .from(instanceMirrors)
95
+ .where(eq(instanceMirrors.id, mirrorId))
96
+ .limit(1);
97
+ return row ? mirrorRowToConfig(row) : null;
98
+ }
99
+ /**
100
+ * Check if inbound content should be accepted by a mirror.
101
+ * Applies content type and tag filters.
102
+ * Returns the mirror ID if accepted, null if rejected.
103
+ *
104
+ * CRITICAL FOR LOOP PREVENTION:
105
+ * - Only accepts content from the mirror's remoteDomain
106
+ * - Rejects content that originated from our own domain (checked upstream in onCreate)
107
+ */
108
+ export async function matchMirrorForContent(db, originDomain, apType, cpubType, tags) {
109
+ // Find active mirror for this origin domain
110
+ const [mirror] = await db
111
+ .select()
112
+ .from(instanceMirrors)
113
+ .where(and(eq(instanceMirrors.remoteDomain, originDomain), eq(instanceMirrors.status, 'active'), eq(instanceMirrors.direction, 'pull')))
114
+ .limit(1);
115
+ if (!mirror)
116
+ return null;
117
+ // Apply content type filter
118
+ if (mirror.filterContentTypes) {
119
+ const allowedTypes = mirror.filterContentTypes;
120
+ const contentType = cpubType ?? apType.toLowerCase();
121
+ if (!allowedTypes.includes(contentType))
122
+ return null;
123
+ }
124
+ // Apply tag filter
125
+ if (mirror.filterTags) {
126
+ const requiredTags = mirror.filterTags;
127
+ const contentTags = tags.map((t) => t.name.toLowerCase().replace(/^#/, ''));
128
+ const hasMatchingTag = requiredTags.some((rt) => contentTags.includes(rt.toLowerCase().replace(/^#/, '')));
129
+ if (!hasMatchingTag)
130
+ return null;
131
+ }
132
+ // Update mirror stats
133
+ await db
134
+ .update(instanceMirrors)
135
+ .set({
136
+ contentCount: sql `${instanceMirrors.contentCount} + 1`,
137
+ lastSyncAt: new Date(),
138
+ updatedAt: new Date(),
139
+ })
140
+ .where(eq(instanceMirrors.id, mirror.id));
141
+ return mirror.id;
142
+ }
143
+ /**
144
+ * Record a mirror error (for admin visibility).
145
+ */
146
+ export async function recordMirrorError(db, mirrorId, error) {
147
+ await db
148
+ .update(instanceMirrors)
149
+ .set({
150
+ errorCount: sql `${instanceMirrors.errorCount} + 1`,
151
+ lastError: error,
152
+ updatedAt: new Date(),
153
+ })
154
+ .where(eq(instanceMirrors.id, mirrorId));
155
+ }
156
+ function mirrorRowToConfig(row) {
157
+ return {
158
+ id: row.id,
159
+ remoteDomain: row.remoteDomain,
160
+ remoteActorUri: row.remoteActorUri,
161
+ status: row.status,
162
+ direction: row.direction,
163
+ filterContentTypes: row.filterContentTypes,
164
+ filterTags: row.filterTags,
165
+ contentCount: row.contentCount,
166
+ errorCount: row.errorCount,
167
+ lastError: row.lastError,
168
+ lastSyncAt: row.lastSyncAt?.toISOString() ?? null,
169
+ createdAt: row.createdAt.toISOString(),
170
+ };
171
+ }
172
+ //# sourceMappingURL=mirroring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirroring.js","sourceRoot":"","sources":["../../src/federation/mirroring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ import { type OAuthAuthorizeRequest, type OAuthTokenRequest, type OAuthDynamicRegistrationRequest } from '@commonpub/protocol';
2
+ import type { DB } from '../types.js';
3
+ export interface AuthorizeResult {
4
+ /** Authorization code to return to the client */
5
+ code: string;
6
+ /** Redirect URI to send the user back to */
7
+ redirectUri: string;
8
+ /** State parameter to pass through */
9
+ state?: string;
10
+ }
11
+ export interface TokenResult {
12
+ accessToken: string;
13
+ tokenType: 'Bearer';
14
+ expiresIn: number;
15
+ /** User info embedded in token response */
16
+ user: {
17
+ id: string;
18
+ username: string;
19
+ displayName: string | null;
20
+ avatarUrl: string | null;
21
+ actorUri: string;
22
+ };
23
+ }
24
+ /**
25
+ * Process an OAuth2 authorization request.
26
+ * Validates the client, generates an auth code, returns redirect info.
27
+ * Called after user consents on the authorize page.
28
+ */
29
+ export declare function processAuthorize(db: DB, params: OAuthAuthorizeRequest, userId: string, domain: string): Promise<AuthorizeResult | {
30
+ error: string;
31
+ errorDescription: string;
32
+ }>;
33
+ /**
34
+ * Exchange an authorization code for an access token.
35
+ * Validates the client credentials and code, returns user info.
36
+ */
37
+ export declare function processTokenExchange(db: DB, params: OAuthTokenRequest, domain: string): Promise<TokenResult | {
38
+ error: string;
39
+ errorDescription: string;
40
+ }>;
41
+ export interface RegisteredClient {
42
+ clientId: string;
43
+ clientSecret: string;
44
+ }
45
+ /**
46
+ * Register this instance as an OAuth client with a remote instance.
47
+ * In v1, this is done manually by admins. Returns client credentials for storage.
48
+ */
49
+ export declare function registerOAuthClient(db: DB, instanceDomain: string, redirectUris: string[]): Promise<RegisteredClient>;
50
+ /**
51
+ * Link a federated account to a local user after successful OAuth callback.
52
+ */
53
+ export declare function linkFederatedAccount(db: DB, userId: string, actorUri: string, instanceDomain: string, profile?: {
54
+ preferredUsername?: string;
55
+ displayName?: string;
56
+ avatarUrl?: string;
57
+ }): Promise<void>;
58
+ /**
59
+ * Find a local user linked to a federated account by actor URI.
60
+ */
61
+ export declare function findUserByFederatedAccount(db: DB, actorUri: string): Promise<{
62
+ userId: string;
63
+ username: string;
64
+ } | null>;
65
+ /**
66
+ * List registered OAuth clients (for admin panel).
67
+ */
68
+ export declare function listOAuthClients(db: DB): Promise<Array<{
69
+ id: string;
70
+ clientId: string;
71
+ instanceDomain: string;
72
+ createdAt: Date;
73
+ }>>;
74
+ /**
75
+ * Process a dynamic OAuth client registration request.
76
+ * Idempotent: if a client already exists for this domain, returns existing credentials.
77
+ */
78
+ export declare function processDynamicRegistration(db: DB, request: OAuthDynamicRegistrationRequest): Promise<{
79
+ clientId: string;
80
+ clientSecret: string;
81
+ } | {
82
+ error: string;
83
+ errorDescription: string;
84
+ }>;
85
+ export interface OAuthLoginState {
86
+ tokenEndpoint: string;
87
+ clientId: string;
88
+ clientSecret: string;
89
+ redirectUri: string;
90
+ instanceDomain: string;
91
+ expiresAt: number;
92
+ }
93
+ /**
94
+ * Store OAuth login state for the federated login flow.
95
+ * Returns a state token that should be passed to the remote authorize endpoint.
96
+ */
97
+ export declare function storeOAuthState(db: DB, state: Omit<OAuthLoginState, 'expiresAt'>): Promise<string>;
98
+ /**
99
+ * Consume OAuth login state (single-use). Returns null if expired or not found.
100
+ */
101
+ export declare function consumeOAuthState(db: DB, stateToken: string): Promise<OAuthLoginState | null>;
102
+ /**
103
+ * Exchange an authorization code with a remote instance's token endpoint.
104
+ */
105
+ export declare function exchangeCodeForToken(state: OAuthLoginState, code: string): Promise<{
106
+ accessToken: string;
107
+ user: {
108
+ id: string;
109
+ username: string;
110
+ displayName: string | null;
111
+ avatarUrl: string | null;
112
+ actorUri: string;
113
+ };
114
+ } | null>;
115
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/federation/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,277 @@
1
+ /**
2
+ * OAuth2 authorization server and client functions for federation SSO.
3
+ * Implements the server side (authorize + token endpoints) and
4
+ * client side (initiate login + handle callback) of cross-instance SSO.
5
+ */
6
+ import { eq } from 'drizzle-orm';
7
+ import { oauthClients, users, federatedAccounts, instanceSettings, } from '@commonpub/schema';
8
+ import { validateAuthorizeRequest, validateTokenRequest, validateDynamicRegistration, } from '@commonpub/protocol';
9
+ import { storeAuthCode, consumeAuthCode } from '../oauthCodes.js';
10
+ /** Generate a cryptographically secure random token (hex-encoded, 256 bits) */
11
+ function generateSecureToken() {
12
+ const bytes = new Uint8Array(32);
13
+ crypto.getRandomValues(bytes);
14
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
15
+ }
16
+ /**
17
+ * Process an OAuth2 authorization request.
18
+ * Validates the client, generates an auth code, returns redirect info.
19
+ * Called after user consents on the authorize page.
20
+ */
21
+ export async function processAuthorize(db, params, userId, domain) {
22
+ // Look up the client
23
+ const [client] = await db
24
+ .select()
25
+ .from(oauthClients)
26
+ .where(eq(oauthClients.clientId, params.clientId))
27
+ .limit(1);
28
+ const oauthClient = client
29
+ ? {
30
+ clientId: client.clientId,
31
+ clientSecret: client.clientSecret,
32
+ redirectUris: client.redirectUris,
33
+ instanceDomain: client.instanceDomain,
34
+ }
35
+ : null;
36
+ // Validate the request
37
+ const validationError = validateAuthorizeRequest(params, oauthClient);
38
+ if (validationError)
39
+ return validationError;
40
+ // Generate authorization code (cryptographically secure, 32 bytes = 256 bits)
41
+ const code = generateSecureToken();
42
+ await storeAuthCode(db, code, userId, params.clientId, params.redirectUri);
43
+ return {
44
+ code,
45
+ redirectUri: params.redirectUri,
46
+ state: params.state,
47
+ };
48
+ }
49
+ /**
50
+ * Exchange an authorization code for an access token.
51
+ * Validates the client credentials and code, returns user info.
52
+ */
53
+ export async function processTokenExchange(db, params, domain) {
54
+ // Look up the client
55
+ const [client] = await db
56
+ .select()
57
+ .from(oauthClients)
58
+ .where(eq(oauthClients.clientId, params.clientId))
59
+ .limit(1);
60
+ const oauthClient = client
61
+ ? {
62
+ clientId: client.clientId,
63
+ clientSecret: client.clientSecret,
64
+ redirectUris: client.redirectUris,
65
+ instanceDomain: client.instanceDomain,
66
+ }
67
+ : null;
68
+ // Validate the request
69
+ const validationError = validateTokenRequest(params, oauthClient);
70
+ if (validationError)
71
+ return validationError;
72
+ // Consume the auth code (single-use)
73
+ const codeResult = await consumeAuthCode(db, params.code, params.clientId, params.redirectUri);
74
+ if (!codeResult) {
75
+ return { error: 'invalid_grant', errorDescription: 'Invalid or expired authorization code' };
76
+ }
77
+ // Look up the user
78
+ const [user] = await db
79
+ .select()
80
+ .from(users)
81
+ .where(eq(users.id, codeResult.userId))
82
+ .limit(1);
83
+ if (!user) {
84
+ return { error: 'server_error', errorDescription: 'User not found' };
85
+ }
86
+ // Generate a cryptographically secure access token
87
+ const accessToken = generateSecureToken();
88
+ const actorUri = `https://${domain}/users/${user.username}`;
89
+ return {
90
+ accessToken,
91
+ tokenType: 'Bearer',
92
+ expiresIn: 3600,
93
+ user: {
94
+ id: user.id,
95
+ username: user.username,
96
+ displayName: user.displayName,
97
+ avatarUrl: user.avatarUrl ?? null,
98
+ actorUri,
99
+ },
100
+ };
101
+ }
102
+ /**
103
+ * Register this instance as an OAuth client with a remote instance.
104
+ * In v1, this is done manually by admins. Returns client credentials for storage.
105
+ */
106
+ export async function registerOAuthClient(db, instanceDomain, redirectUris) {
107
+ const clientId = `cpub_${generateSecureToken().slice(0, 32)}`;
108
+ const clientSecret = `cpubs_${generateSecureToken()}`;
109
+ await db.insert(oauthClients).values({
110
+ clientId,
111
+ clientSecret,
112
+ redirectUris,
113
+ instanceDomain,
114
+ });
115
+ return { clientId, clientSecret };
116
+ }
117
+ /**
118
+ * Link a federated account to a local user after successful OAuth callback.
119
+ */
120
+ export async function linkFederatedAccount(db, userId, actorUri, instanceDomain, profile) {
121
+ // Check if already linked
122
+ const existing = await db
123
+ .select()
124
+ .from(federatedAccounts)
125
+ .where(eq(federatedAccounts.actorUri, actorUri))
126
+ .limit(1);
127
+ if (existing.length > 0) {
128
+ // Update the link
129
+ await db
130
+ .update(federatedAccounts)
131
+ .set({
132
+ userId,
133
+ lastSyncedAt: new Date(),
134
+ ...(profile?.preferredUsername && { preferredUsername: profile.preferredUsername }),
135
+ ...(profile?.displayName && { displayName: profile.displayName }),
136
+ ...(profile?.avatarUrl && { avatarUrl: profile.avatarUrl }),
137
+ })
138
+ .where(eq(federatedAccounts.actorUri, actorUri));
139
+ }
140
+ else {
141
+ await db.insert(federatedAccounts).values({
142
+ userId,
143
+ actorUri,
144
+ instanceDomain,
145
+ preferredUsername: profile?.preferredUsername,
146
+ displayName: profile?.displayName,
147
+ avatarUrl: profile?.avatarUrl,
148
+ lastSyncedAt: new Date(),
149
+ });
150
+ }
151
+ }
152
+ /**
153
+ * Find a local user linked to a federated account by actor URI.
154
+ */
155
+ export async function findUserByFederatedAccount(db, actorUri) {
156
+ const rows = await db
157
+ .select({
158
+ userId: federatedAccounts.userId,
159
+ username: users.username,
160
+ })
161
+ .from(federatedAccounts)
162
+ .innerJoin(users, eq(federatedAccounts.userId, users.id))
163
+ .where(eq(federatedAccounts.actorUri, actorUri))
164
+ .limit(1);
165
+ return rows[0] ?? null;
166
+ }
167
+ /**
168
+ * List registered OAuth clients (for admin panel).
169
+ */
170
+ export async function listOAuthClients(db) {
171
+ return db
172
+ .select({
173
+ id: oauthClients.id,
174
+ clientId: oauthClients.clientId,
175
+ instanceDomain: oauthClients.instanceDomain,
176
+ createdAt: oauthClients.createdAt,
177
+ })
178
+ .from(oauthClients);
179
+ }
180
+ // --- Dynamic Client Registration ---
181
+ /**
182
+ * Process a dynamic OAuth client registration request.
183
+ * Idempotent: if a client already exists for this domain, returns existing credentials.
184
+ */
185
+ export async function processDynamicRegistration(db, request) {
186
+ const validationError = validateDynamicRegistration(request);
187
+ if (validationError)
188
+ return validationError;
189
+ // Check for existing client with this domain (idempotent)
190
+ const [existing] = await db
191
+ .select()
192
+ .from(oauthClients)
193
+ .where(eq(oauthClients.instanceDomain, request.instanceDomain))
194
+ .limit(1);
195
+ if (existing) {
196
+ return { clientId: existing.clientId, clientSecret: existing.clientSecret };
197
+ }
198
+ // Register new client
199
+ const result = await registerOAuthClient(db, request.instanceDomain, request.redirectUris);
200
+ return result;
201
+ }
202
+ // --- OAuth State Management (for federated login flow) ---
203
+ const OAUTH_STATE_PREFIX = 'oauth_state:';
204
+ const OAUTH_STATE_TTL_MS = 10 * 60 * 1000; // 10 minutes
205
+ /**
206
+ * Store OAuth login state for the federated login flow.
207
+ * Returns a state token that should be passed to the remote authorize endpoint.
208
+ */
209
+ export async function storeOAuthState(db, state) {
210
+ const stateToken = generateSecureToken();
211
+ const key = `${OAUTH_STATE_PREFIX}${stateToken}`;
212
+ await db.insert(instanceSettings).values({
213
+ key,
214
+ value: {
215
+ ...state,
216
+ expiresAt: Date.now() + OAUTH_STATE_TTL_MS,
217
+ },
218
+ });
219
+ return stateToken;
220
+ }
221
+ /**
222
+ * Consume OAuth login state (single-use). Returns null if expired or not found.
223
+ */
224
+ export async function consumeOAuthState(db, stateToken) {
225
+ const key = `${OAUTH_STATE_PREFIX}${stateToken}`;
226
+ const rows = await db
227
+ .select()
228
+ .from(instanceSettings)
229
+ .where(eq(instanceSettings.key, key))
230
+ .limit(1);
231
+ if (rows.length === 0)
232
+ return null;
233
+ // Delete immediately (single-use)
234
+ await db.delete(instanceSettings).where(eq(instanceSettings.key, key));
235
+ const state = rows[0].value;
236
+ // Check expiry
237
+ if (Date.now() > state.expiresAt)
238
+ return null;
239
+ return state;
240
+ }
241
+ /**
242
+ * Exchange an authorization code with a remote instance's token endpoint.
243
+ */
244
+ export async function exchangeCodeForToken(state, code) {
245
+ try {
246
+ const response = await fetch(state.tokenEndpoint, {
247
+ method: 'POST',
248
+ headers: { 'Content-Type': 'application/json' },
249
+ body: JSON.stringify({
250
+ grant_type: 'authorization_code',
251
+ code,
252
+ client_id: state.clientId,
253
+ client_secret: state.clientSecret,
254
+ redirect_uri: state.redirectUri,
255
+ }),
256
+ });
257
+ if (!response.ok)
258
+ return null;
259
+ const data = await response.json();
260
+ if (!data.access_token || !data.user)
261
+ return null;
262
+ return {
263
+ accessToken: data.access_token,
264
+ user: {
265
+ id: data.user.id,
266
+ username: data.user.username,
267
+ displayName: data.user.displayName ?? null,
268
+ avatarUrl: data.user.avatarUrl ?? null,
269
+ actorUri: data.user.actorUri ?? `https://${state.instanceDomain}/users/${data.user.username}`,
270
+ },
271
+ };
272
+ }
273
+ catch {
274
+ return null;
275
+ }
276
+ }
277
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/federation/oauth.ts"],"names":[],"mappings":"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"}