+6
-5
apps/main-app/package.json
+6
-5
apps/main-app/package.json
···
11
"screenshot": "bun run scripts/screenshot-sites.ts"
12
},
13
"dependencies": {
14
-
"@atproto/api": "^0.17.3",
15
-
"@atproto/common-web": "^0.4.5",
16
"@atproto/jwk-jose": "^0.1.11",
17
-
"@atproto/lex-cli": "^0.9.5",
18
-
"@atproto/oauth-client-node": "^0.3.9",
19
-
"@atproto/xrpc-server": "^0.9.5",
20
"@elysiajs/cors": "^1.4.0",
21
"@elysiajs/eden": "^1.4.3",
22
"@elysiajs/openapi": "^1.4.11",
···
11
"screenshot": "bun run scripts/screenshot-sites.ts"
12
},
13
"dependencies": {
14
+
"@atproto-labs/did-resolver": "^0.2.4",
15
+
"@atproto/api": "^0.17.7",
16
+
"@atproto/common-web": "^0.4.6",
17
"@atproto/jwk-jose": "^0.1.11",
18
+
"@atproto/lex-cli": "^0.9.7",
19
+
"@atproto/oauth-client-node": "^0.3.12",
20
+
"@atproto/xrpc-server": "^0.9.6",
21
"@elysiajs/cors": "^1.4.0",
22
"@elysiajs/eden": "^1.4.3",
23
"@elysiajs/openapi": "^1.4.11",
+5
-4
apps/main-app/src/lib/oauth-client.ts
+5
-4
apps/main-app/src/lib/oauth-client.ts
···
4
import { logger } from "./logger";
5
import { SlingshotHandleResolver } from "./slingshot-handle-resolver";
6
7
// Session timeout configuration (30 days in seconds)
8
const SESSION_TIMEOUT = 30 * 24 * 60 * 60; // 2592000 seconds
9
// OAuth state timeout (1 hour in seconds)
···
110
// Loopback client for local development
111
// For loopback, scopes and redirect_uri must be in client_id query string
112
const redirectUri = 'http://127.0.0.1:8000/api/auth/callback';
113
-
const scope = 'atproto repo:place.wisp.fs repo:place.wisp.domain repo:place.wisp.subfs repo:place.wisp.settings blob:*/* rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app#bsky_appview';
114
const params = new URLSearchParams();
115
params.append('redirect_uri', redirectUri);
116
-
params.append('scope', scope);
117
118
return {
119
client_id: `http://localhost?${params.toString()}`,
···
124
response_types: ['code'],
125
application_type: 'web',
126
token_endpoint_auth_method: 'none',
127
-
scope: scope,
128
dpop_bound_access_tokens: false,
129
subject_type: 'public',
130
authorization_signed_response_alg: 'ES256'
···
145
application_type: 'web',
146
token_endpoint_auth_method: 'private_key_jwt',
147
token_endpoint_auth_signing_alg: "ES256",
148
-
scope: "atproto repo:place.wisp.fs repo:place.wisp.domain repo:place.wisp.subfs repo:place.wisp.settings blob:*/* rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app#bsky_appview",
149
dpop_bound_access_tokens: true,
150
jwks_uri: `${config.domain}/jwks.json`,
151
subject_type: 'public',
···
4
import { logger } from "./logger";
5
import { SlingshotHandleResolver } from "./slingshot-handle-resolver";
6
7
+
// OAuth scope for all client types
8
+
const OAUTH_SCOPE = 'atproto repo:place.wisp.fs repo:place.wisp.domain repo:place.wisp.subfs repo:place.wisp.settings blob:*/*';
9
// Session timeout configuration (30 days in seconds)
10
const SESSION_TIMEOUT = 30 * 24 * 60 * 60; // 2592000 seconds
11
// OAuth state timeout (1 hour in seconds)
···
112
// Loopback client for local development
113
// For loopback, scopes and redirect_uri must be in client_id query string
114
const redirectUri = 'http://127.0.0.1:8000/api/auth/callback';
115
const params = new URLSearchParams();
116
params.append('redirect_uri', redirectUri);
117
+
params.append('scope', OAUTH_SCOPE);
118
119
return {
120
client_id: `http://localhost?${params.toString()}`,
···
125
response_types: ['code'],
126
application_type: 'web',
127
token_endpoint_auth_method: 'none',
128
+
scope: OAUTH_SCOPE,
129
dpop_bound_access_tokens: false,
130
subject_type: 'public',
131
authorization_signed_response_alg: 'ES256'
···
146
application_type: 'web',
147
token_endpoint_auth_method: 'private_key_jwt',
148
token_endpoint_auth_signing_alg: "ES256",
149
+
scope: OAUTH_SCOPE,
150
dpop_bound_access_tokens: true,
151
jwks_uri: `${config.domain}/jwks.json`,
152
subject_type: 'public',
+10
-12
apps/main-app/src/routes/user.ts
+10
-12
apps/main-app/src/routes/user.ts
···
1
import { Elysia, t } from 'elysia'
2
import { requireAuth } from '../lib/wisp-auth'
3
import { NodeOAuthClient } from '@atproto/oauth-client-node'
4
-
import { Agent } from '@atproto/api'
5
import { getSitesByDid, getDomainByDid, getCustomDomainsByDid, getWispDomainInfo, getDomainsBySite, getAllWispDomains } from '../lib/db'
6
import { syncSitesFromPDS } from '../lib/sync-sites'
7
import { createLogger } from '@wisp/observability'
8
9
const logger = createLogger('main-app')
10
11
export const userRoutes = (client: NodeOAuthClient, cookieSecret: string) =>
12
new Elysia({
···
42
})
43
.get('/info', async ({ auth }) => {
44
try {
45
-
// Get user's handle from AT Protocol
46
-
const agent = new Agent(auth.session)
47
-
48
let handle = 'unknown'
49
try {
50
-
console.log('[User] Attempting to fetch profile for DID:', auth.did)
51
-
const profile = await agent.getProfile({ actor: auth.did })
52
-
console.log('[User] Profile fetched successfully:', profile.data.handle)
53
-
handle = profile.data.handle
54
} catch (err) {
55
-
console.error('[User] Failed to fetch profile - Full error:', err)
56
-
console.error('[User] Error message:', err instanceof Error ? err.message : String(err))
57
-
console.error('[User] Error stack:', err instanceof Error ? err.stack : 'No stack')
58
-
logger.error('[User] Failed to fetch profile', err)
59
}
60
61
return {
···
1
import { Elysia, t } from 'elysia'
2
import { requireAuth } from '../lib/wisp-auth'
3
import { NodeOAuthClient } from '@atproto/oauth-client-node'
4
import { getSitesByDid, getDomainByDid, getCustomDomainsByDid, getWispDomainInfo, getDomainsBySite, getAllWispDomains } from '../lib/db'
5
import { syncSitesFromPDS } from '../lib/sync-sites'
6
import { createLogger } from '@wisp/observability'
7
+
import { createDidResolver, extractAtprotoData } from '@atproto-labs/did-resolver'
8
9
const logger = createLogger('main-app')
10
+
const didResolver = createDidResolver({})
11
12
export const userRoutes = (client: NodeOAuthClient, cookieSecret: string) =>
13
new Elysia({
···
43
})
44
.get('/info', async ({ auth }) => {
45
try {
46
let handle = 'unknown'
47
try {
48
+
const didDoc = await didResolver.resolve(auth.did)
49
+
const atprotoData = extractAtprotoData(didDoc)
50
+
51
+
if (atprotoData.aka) {
52
+
handle = atprotoData.aka
53
+
}
54
} catch (err) {
55
+
56
+
logger.error('[User] Failed to resolve DID', err)
57
}
58
59
return {
+6
-5
bun.lock
+6
-5
bun.lock
···
45
"name": "@wisp/main-app",
46
"version": "1.0.50",
47
"dependencies": {
48
-
"@atproto/api": "^0.17.3",
49
-
"@atproto/common-web": "^0.4.5",
50
"@atproto/jwk-jose": "^0.1.11",
51
-
"@atproto/lex-cli": "^0.9.5",
52
-
"@atproto/oauth-client-node": "^0.3.9",
53
-
"@atproto/xrpc-server": "^0.9.5",
54
"@elysiajs/cors": "^1.4.0",
55
"@elysiajs/eden": "^1.4.3",
56
"@elysiajs/openapi": "^1.4.11",
···
45
"name": "@wisp/main-app",
46
"version": "1.0.50",
47
"dependencies": {
48
+
"@atproto-labs/did-resolver": "^0.2.4",
49
+
"@atproto/api": "^0.17.7",
50
+
"@atproto/common-web": "^0.4.6",
51
"@atproto/jwk-jose": "^0.1.11",
52
+
"@atproto/lex-cli": "^0.9.7",
53
+
"@atproto/oauth-client-node": "^0.3.12",
54
+
"@atproto/xrpc-server": "^0.9.6",
55
"@elysiajs/cors": "^1.4.0",
56
"@elysiajs/eden": "^1.4.3",
57
"@elysiajs/openapi": "^1.4.11",