Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion server/api/atproto/bluesky-author-profiles.get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AuthorSchema } from '#shared/schemas/blog'
import { Client } from '@atproto/lex'
import type { Author, ResolvedAuthor } from '#shared/schemas/blog'
import * as app from '#shared/types/lexicons/app'
import * as crypto from 'node:crypto'

export default defineCachedEventHandler(
async event => {
Expand Down Expand Up @@ -75,7 +76,11 @@ export default defineCachedEventHandler(
maxAge: CACHE_MAX_AGE_ONE_DAY,
getKey: event => {
const { authors } = getQuery(event)
return `author-profiles:${authors ?? 'npmx.dev'}`
if (!authors) {
return 'author-profiles:npmx.dev'
}
const key = crypto.createHash('sha256').update(JSON.stringify(authors)).digest('hex')
return `author-profiles:${key}`
},
},
)
9 changes: 8 additions & 1 deletion server/api/auth/atproto.get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,14 @@ export default defineEventHandler(async event => {
// Handle callback
try {
const params = new URLSearchParams(query as Record<string, string>)
const result = await event.context.oauthClient.callback(params)
const result = await event.context.oauthClient?.callback(params)
if (!result) {
return handleApiError('Failed to initiate authentication', {
statusCode: 401,
statusMessage: 'Unauthorized',
message: `Failed to initiate authentication. Please login and try again.`,
})
}
try {
const state = decodeOAuthState(event, result.state)
const profile = await getMiniProfile(result.session)
Expand Down
16 changes: 11 additions & 5 deletions server/plugins/oauth-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import type { NodeOAuthClient } from '@atproto/oauth-client-node'
* Creates a long living instance of the NodeOAuthClient.
*/
export default defineNitroPlugin(async nitroApp => {
const oauthClient = await getNodeOAuthClient()
try {
const oauthClient = await getNodeOAuthClient()

// Attach to event context for access in composables via useRequestEvent()
nitroApp.hooks.hook('request', event => {
event.context.oauthClient = oauthClient
})
// Attach to event context for access in composables via useRequestEvent()
nitroApp.hooks.hook('request', event => {
event.context.oauthClient = oauthClient
})
} catch (e) {
if (!import.meta.test) {
throw e
}
}
})

// Extend the H3EventContext type
Expand Down
1 change: 0 additions & 1 deletion server/routes/.well-known/jwks.json.get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { loadJWKs } from '#server/utils/atproto/oauth'
export default defineEventHandler(async _ => {
const keys = await loadJWKs()
if (!keys) {
console.error('Failed to load JWKs. May not be set')
return []
}

Expand Down
10 changes: 8 additions & 2 deletions server/utils/atproto/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,13 @@ export async function loadJWKs(): Promise<Keyset | undefined> {
// If we ever need to add multiple JWKs to rotate keys we will need to add a new one
// under a new variable and update here
const jwkOne = useRuntimeConfig().oauthJwkOne
if (!jwkOne) return undefined
if (!jwkOne) {
if (!import.meta.test) {
// eslint-disable-next-line no-console
console.error('Failed to load JWKs (not set).')
}
return undefined
}

// For multiple keys if we need to rotate
// const keys = await Promise.all([JoseKey.fromImportable(jwkOne)])
Expand All @@ -111,7 +117,7 @@ async function getOAuthSession(event: H3Event): Promise<{
return { oauthSession: undefined, serverSession }
}

const oauthSession = await event.context.oauthClient.restore(currentSession.public.did)
const oauthSession = await event.context.oauthClient?.restore(currentSession.public.did)
return { oauthSession, serverSession }
} catch (error) {
// Log error safely without using util.inspect on potentially problematic objects
Expand Down
Loading