diff --git a/server/api/atproto/bluesky-author-profiles.get.ts b/server/api/atproto/bluesky-author-profiles.get.ts index 1122376d82..4499a02a61 100644 --- a/server/api/atproto/bluesky-author-profiles.get.ts +++ b/server/api/atproto/bluesky-author-profiles.get.ts @@ -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 => { @@ -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}` }, }, ) diff --git a/server/api/auth/atproto.get.ts b/server/api/auth/atproto.get.ts index 64137bdd4e..22dbf8372f 100644 --- a/server/api/auth/atproto.get.ts +++ b/server/api/auth/atproto.get.ts @@ -80,7 +80,14 @@ export default defineEventHandler(async event => { // Handle callback try { const params = new URLSearchParams(query as Record) - 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) diff --git a/server/plugins/oauth-client.ts b/server/plugins/oauth-client.ts index 7f888271ba..874e9e74d5 100644 --- a/server/plugins/oauth-client.ts +++ b/server/plugins/oauth-client.ts @@ -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 diff --git a/server/routes/.well-known/jwks.json.get.ts b/server/routes/.well-known/jwks.json.get.ts index f7c2eda628..129ed5c16e 100644 --- a/server/routes/.well-known/jwks.json.get.ts +++ b/server/routes/.well-known/jwks.json.get.ts @@ -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 [] } diff --git a/server/utils/atproto/oauth.ts b/server/utils/atproto/oauth.ts index 58152fd2b1..b947b3df96 100644 --- a/server/utils/atproto/oauth.ts +++ b/server/utils/atproto/oauth.ts @@ -89,7 +89,13 @@ export async function loadJWKs(): Promise { // 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)]) @@ -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