Skip to content

Commit d3d91f8

Browse files
committed
I think that's the session store
Should be state store as well should be conf client commiting to swap I think that's the redirect appeasing my robotic overlords in the ci resolves setting a new session id everytime that rabbit redis cache and other updates pretend you don't see this till Saturday. I'll be at a football game also pretend like you didnt see this. changed to useRunTimeConfig for jwk
1 parent 0b17ba9 commit d3d91f8

18 files changed

Lines changed: 220 additions & 126 deletions

app/components/Header/AuthModal.client.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ watch(handleInput, newHandleInput => {
5050
handleInput.value = normalized
5151
}
5252
})
53+
54+
watch(user, async newUser => {
55+
if (newUser?.relogin) {
56+
await authRedirect(newUser.did, {
57+
redirectTo: route.fullPath,
58+
})
59+
}
60+
})
5361
</script>
5462

5563
<template>

modules/cache.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { provider } from 'std-env'
44

55
// Storage key for fetch cache - must match shared/utils/fetch-cache-config.ts
66
const FETCH_CACHE_STORAGE_BASE = 'fetch-cache'
7+
// Storage key for OAuth cache - must match server/utils/atproto/storage.ts
8+
const OAUTH_CACHE_STORAGE_BASE = 'atproto:oauth'
79

810
export default defineNuxtModule({
911
meta: {
@@ -37,6 +39,11 @@ export default defineNuxtModule({
3739
...nitroConfig.storage[FETCH_CACHE_STORAGE_BASE],
3840
driver: 'vercel-runtime-cache',
3941
}
42+
43+
nitroConfig.storage[OAUTH_CACHE_STORAGE_BASE] = {
44+
...nitroConfig.storage[OAUTH_CACHE_STORAGE_BASE],
45+
driver: 'vercel-runtime-cache',
46+
}
4047
}
4148

4249
const env = process.env.VERCEL_ENV

nuxt.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export default defineNuxtConfig({
3737
github: {
3838
orgToken: '',
3939
},
40+
oauthJwkOne: process.env.OAUTH_JWK_ONE || undefined,
4041
// Upstash Redis for distributed OAuth token refresh locking in production
4142
upstash: {
4243
redisRestUrl: process.env.UPSTASH_KV_REST_API_URL || process.env.KV_REST_API_URL || '',
@@ -122,6 +123,7 @@ export default defineNuxtConfig({
122123
'/_avatar/**': { isr: 3600, proxy: 'https://www.gravatar.com/avatar/**' },
123124
'/opensearch.xml': { isr: true },
124125
'/oauth-client-metadata.json': { prerender: true },
126+
'/.well-known/jwks.json': { prerender: true },
125127
// never cache
126128
'/api/auth/**': { isr: false, cache: false },
127129
'/api/social/**': { isr: false, cache: false },

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"generate:sprite": "node scripts/generate-file-tree-sprite.ts",
3535
"generate:fixtures": "node scripts/generate-fixtures.ts",
3636
"generate:lexicons": "lex build --lexicons lexicons --out shared/types/lexicons --clear",
37+
"generate:jwk": "node scripts/gen-jwk.ts",
3738
"test": "vite test",
3839
"test:a11y": "pnpm build:test && LIGHTHOUSE_COLOR_MODE=dark pnpm test:a11y:prebuilt && LIGHTHOUSE_COLOR_MODE=light pnpm test:a11y:prebuilt",
3940
"test:a11y:prebuilt": "./scripts/lighthouse.sh",

scripts/gen-jwk.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { JoseKey } from '@atproto/oauth-client-node'
2+
3+
async function run() {
4+
const kid = Date.now().toString()
5+
const key = await JoseKey.generate(['ES256'], kid)
6+
const jwk = key.privateJwk
7+
8+
console.log(JSON.stringify(jwk))
9+
}
10+
11+
await run()

server/api/auth/atproto.get.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import type { OAuthSession } from '@atproto/oauth-client-node'
2-
import { NodeOAuthClient, OAuthCallbackError } from '@atproto/oauth-client-node'
2+
import { OAuthCallbackError } from '@atproto/oauth-client-node'
33
import { createError, getQuery, sendRedirect, setCookie, getCookie, deleteCookie } from 'h3'
44
import type { H3Event } from 'h3'
5-
import { getOAuthLock } from '#server/utils/atproto/lock'
6-
import { useOAuthStorage } from '#server/utils/atproto/storage'
75
import { SLINGSHOT_HOST } from '#shared/utils/constants'
86
import { useServerSession } from '#server/utils/server-session'
9-
import { handleResolver } from '#server/utils/atproto/oauth'
107
import { handleApiError } from '#server/utils/error-handler'
118
import type { DidString } from '@atproto/lex'
129
import { Client } from '@atproto/lex'
1310
import * as com from '#shared/types/lexicons/com'
1411
import * as app from '#shared/types/lexicons/app'
1512
import { isAtIdentifierString } from '@atproto/lex'
16-
import { scope, getOauthClientMetadata } from '#server/utils/atproto/oauth'
13+
import { scope } from '#server/utils/atproto/oauth'
1714
import { UNSET_NUXT_SESSION_PASSWORD } from '#shared/utils/constants'
1815
// @ts-expect-error virtual file from oauth module
1916
import { clientUri } from '#oauth/config'
@@ -28,17 +25,7 @@ export default defineEventHandler(async event => {
2825
}
2926

3027
const query = getQuery(event)
31-
const clientMetadata = getOauthClientMetadata()
3228
const session = await useServerSession(event)
33-
const { stateStore, sessionStore } = useOAuthStorage(session)
34-
35-
const atclient = new NodeOAuthClient({
36-
stateStore,
37-
sessionStore,
38-
clientMetadata,
39-
requestLock: getOAuthLock(),
40-
handleResolver,
41-
})
4229

4330
if (query.handle) {
4431
// Initiate auth flow
@@ -66,10 +53,13 @@ export default defineEventHandler(async event => {
6653
}
6754

6855
try {
69-
const redirectUrl = await atclient.authorize(query.handle, {
56+
const redirectUrl = await event.context.oauthClient.authorize(query.handle, {
7057
scope,
7158
prompt: query.create ? 'create' : undefined,
72-
ui_locales: query.locale?.toString(),
59+
// TODO: I do not beleive this is working as expected on
60+
// a unsupported locale on the PDS. Gives Invalid at body.ui_locales
61+
// Commenting out for now
62+
// ui_locales: query.locale?.toString(),
7363
state: encodeOAuthState(event, { redirectPath }),
7464
})
7565

@@ -87,7 +77,7 @@ export default defineEventHandler(async event => {
8777
// Handle callback
8878
try {
8979
const params = new URLSearchParams(query as Record<string, string>)
90-
const result = await atclient.callback(params)
80+
const result = await event.context.oauthClient.callback(params)
9181
try {
9282
const state = decodeOAuthState(event, result.state)
9383
const profile = await getMiniProfile(result.session)

server/api/auth/session.get.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
import { PublicUserSessionSchema } from '#shared/schemas/publicUserSession'
22
import { safeParse } from 'valibot'
33

4-
export default defineEventHandler(async event => {
5-
const serverSession = await useServerSession(event)
4+
export default eventHandlerWithOAuthSession(async (event, _, serverSession) => {
65
const result = safeParse(PublicUserSessionSchema, serverSession.data.public)
76
if (!result.success) {
87
return null
98
}
109

10+
// A one time redirect to upgrade the previous sessions.
11+
// Can remove in 2 weeks from merge if we'd like
12+
if (serverSession.data.oauthSession && serverSession.data?.public?.did) {
13+
await serverSession.update({
14+
oauthSession: undefined,
15+
})
16+
return {
17+
...result.output,
18+
relogin: true,
19+
}
20+
}
21+
1122
return result.output
1223
})

server/plugins/oauth-client.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { NodeOAuthClient } from '@atproto/oauth-client-node'
2+
3+
/**
4+
* Creates a long living instance of the NodeOAuthClient.
5+
*/
6+
export default defineNitroPlugin(async nitroApp => {
7+
const oauthClient = await getNodeOAuthClient()
8+
9+
// Attach to event context for access in composables via useRequestEvent()
10+
nitroApp.hooks.hook('request', event => {
11+
event.context.oauthClient = oauthClient
12+
})
13+
})
14+
15+
// Extend the H3EventContext type
16+
declare module 'h3' {
17+
interface H3EventContext {
18+
oauthClient: NodeOAuthClient
19+
}
20+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { loadJWKs } from '#server/utils/atproto/oauth'
2+
3+
export default defineEventHandler(async _ => {
4+
const keys = await loadJWKs()
5+
if (!keys) {
6+
console.error('Failed to load JWKs. May not be set')
7+
return []
8+
}
9+
10+
return keys.publicJwks
11+
})
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
export default defineEventHandler(() => {
2-
return getOauthClientMetadata()
1+
export default defineEventHandler(async _ => {
2+
const keyset = await loadJWKs()
3+
const pk = keyset?.findPrivateKey({ usage: 'sign' })
4+
return getOauthClientMetadata(pk?.alg)
35
})

0 commit comments

Comments
 (0)