Skip to content

Commit b85af80

Browse files
refactor: moved the oauth to useSession (#867)
Co-authored-by: Daniel Roe <daniel@roe.dev>
1 parent 07ce0e4 commit b85af80

File tree

13 files changed

+97
-105
lines changed

13 files changed

+97
-105
lines changed

app/composables/useAtproto.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import type { UserSession } from '#shared/schemas/userSession'
2-
31
export function useAtproto() {
42
const {
53
data: user,
64
pending,
75
clear,
8-
} = useFetch<UserSession | null>('/api/auth/session', {
6+
} = useFetch('/api/auth/session', {
97
server: false,
108
immediate: !import.meta.test,
119
})

modules/cache.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,6 @@ export default defineNuxtModule({
2727
...nitroConfig.storage[FETCH_CACHE_STORAGE_BASE],
2828
driver: 'vercel-runtime-cache',
2929
}
30-
31-
const env = process.env.VERCEL_ENV
32-
33-
nitroConfig.storage['oauth-atproto-state'] = {
34-
driver: env === 'production' ? 'vercel-kv' : 'vercel-runtime-cache',
35-
}
36-
37-
nitroConfig.storage['oauth-atproto-session'] = {
38-
driver: env === 'production' ? 'vercel-kv' : 'vercel-runtime-cache',
39-
}
4030
})
4131
},
4232
})

nuxt.config.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,6 @@ export default defineNuxtConfig({
151151
driver: 'fsLite',
152152
base: './.cache/fetch',
153153
},
154-
'oauth-atproto-state': {
155-
driver: 'fsLite',
156-
base: './.cache/atproto-oauth/state',
157-
},
158-
'oauth-atproto-session': {
159-
driver: 'fsLite',
160-
base: './.cache/atproto-oauth/session',
161-
},
162154
},
163155
typescript: {
164156
tsConfig: {

server/api/auth/atproto.get.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { NodeOAuthClient } from '@atproto/oauth-client-node'
33
import { createError, getQuery, sendRedirect } from 'h3'
44
import { useOAuthStorage } from '#server/utils/atproto/storage'
55
import { SLINGSHOT_HOST } from '#shared/utils/constants'
6-
import type { UserSession } from '#shared/schemas/userSession'
6+
import { useServerSession } from '#server/utils/server-session'
7+
import type { PublicUserSession } from '#shared/schemas/publicUserSession'
78

89
export default defineEventHandler(async event => {
910
const config = useRuntimeConfig(event)
@@ -16,7 +17,8 @@ export default defineEventHandler(async event => {
1617

1718
const query = getQuery(event)
1819
const clientMetadata = getOauthClientMetadata()
19-
const { stateStore, sessionStore } = useOAuthStorage(event)
20+
const session = await useServerSession(event)
21+
const { stateStore, sessionStore } = useOAuthStorage(session)
2022

2123
const atclient = new NodeOAuthClient({
2224
stateStore,
@@ -48,17 +50,16 @@ export default defineEventHandler(async event => {
4850
const agent = new Agent(authSession)
4951
event.context.agent = agent
5052

51-
const session = await useSession(event, {
52-
password: config.sessionPassword,
53-
})
54-
5553
const response = await fetch(
5654
`https://${SLINGSHOT_HOST}/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${agent.did}`,
5755
{ headers: { 'User-Agent': 'npmx' } },
5856
)
59-
const miniDoc = (await response.json()) as UserSession
60-
61-
await session.update(miniDoc)
57+
if (response.ok) {
58+
const miniDoc: PublicUserSession = await response.json()
59+
await session.update({
60+
public: miniDoc,
61+
})
62+
}
6263

6364
return sendRedirect(event, '/')
6465
})

server/api/auth/session.delete.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export default eventHandlerWithOAuthSession(async (event, oAuthSession, serverSession) => {
2-
await Promise.all([oAuthSession?.signOut(), serverSession.clear()])
3-
2+
// Even tho the signOut also clears part of the server cache should be done in order
3+
// to let the oAuth package do any other clean up it may need
4+
await oAuthSession?.signOut()
5+
await serverSession.clear()
46
return 'Session cleared'
57
})

server/api/auth/session.get.ts

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

44
export default eventHandlerWithOAuthSession(async (event, oAuthSession, serverSession) => {
5-
const result = safeParse(UserSessionSchema, serverSession.data)
5+
const result = safeParse(PublicUserSessionSchema, serverSession.data.public)
66
if (!result.success) {
77
return null
88
}
Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,30 @@
11
import type { NodeSavedSession, NodeSavedSessionStore } from '@atproto/oauth-client-node'
2-
import type { H3Event } from 'h3'
3-
4-
/**
5-
* Storage key prefix for oauth session storage.
6-
*/
7-
export const OAUTH_SESSION_CACHE_STORAGE_BASE = 'oauth-atproto-session'
2+
import type { UserServerSession } from '#shared/types/userSession'
3+
import type { SessionManager } from 'h3'
84

95
export class OAuthSessionStore implements NodeSavedSessionStore {
10-
// TODO: not sure if we will support multi accounts, but if we do in the future will need to change this around
11-
private readonly cookieKey = 'oauth:atproto:session'
12-
private readonly storage = useStorage(OAUTH_SESSION_CACHE_STORAGE_BASE)
6+
private readonly session: SessionManager<UserServerSession>
137

14-
constructor(private event: H3Event) {}
8+
constructor(session: SessionManager<UserServerSession>) {
9+
this.session = session
10+
}
1511

1612
async get(): Promise<NodeSavedSession | undefined> {
17-
const sessionKey = getCookie(this.event, this.cookieKey)
18-
if (!sessionKey) return
19-
const result = await this.storage.getItem<NodeSavedSession>(sessionKey)
20-
if (!result) return
21-
return result
13+
const sessionData = this.session.data
14+
if (!sessionData) return undefined
15+
return sessionData.oauthSession
2216
}
2317

24-
async set(key: string, val: NodeSavedSession) {
25-
setCookie(this.event, this.cookieKey, key, {
26-
httpOnly: true,
27-
secure: !import.meta.dev,
28-
sameSite: 'lax',
18+
async set(_key: string, val: NodeSavedSession) {
19+
// We are ignoring the key since the mapping is already done in the session
20+
await this.session.update({
21+
oauthSession: val,
2922
})
30-
await this.storage.setItem<NodeSavedSession>(key, val)
3123
}
3224

3325
async del() {
34-
const sessionKey = getCookie(this.event, this.cookieKey)
35-
if (sessionKey) {
36-
await this.storage.del(sessionKey)
37-
}
38-
deleteCookie(this.event, this.cookieKey)
26+
await this.session.update({
27+
oauthSession: undefined,
28+
})
3929
}
4030
}
Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,30 @@
11
import type { NodeSavedState, NodeSavedStateStore } from '@atproto/oauth-client-node'
2-
import type { H3Event } from 'h3'
3-
4-
/**
5-
* Storage key prefix for oauth state storage.
6-
*/
7-
export const OAUTH_STATE_CACHE_STORAGE_BASE = 'oauth-atproto-state'
2+
import type { UserServerSession } from '#shared/types/userSession'
3+
import type { SessionManager } from 'h3'
84

95
export class OAuthStateStore implements NodeSavedStateStore {
10-
private readonly cookieKey = 'oauth:atproto:state'
11-
private readonly storage = useStorage(OAUTH_STATE_CACHE_STORAGE_BASE)
6+
private readonly session: SessionManager<UserServerSession>
127

13-
constructor(private event: H3Event) {}
8+
constructor(session: SessionManager<UserServerSession>) {
9+
this.session = session
10+
}
1411

1512
async get(): Promise<NodeSavedState | undefined> {
16-
const stateKey = getCookie(this.event, this.cookieKey)
17-
if (!stateKey) return
18-
const result = await this.storage.getItem<NodeSavedState>(stateKey)
19-
if (!result) return
20-
return result
13+
const sessionData = this.session.data
14+
if (!sessionData) return undefined
15+
return sessionData.oauthState
2116
}
2217

23-
async set(key: string, val: NodeSavedState) {
24-
setCookie(this.event, this.cookieKey, key, {
25-
httpOnly: true,
26-
secure: !import.meta.dev,
27-
sameSite: 'lax',
18+
async set(_key: string, val: NodeSavedState) {
19+
// We are ignoring the key since the mapping is already done in the session
20+
await this.session.update({
21+
oauthState: val,
2822
})
29-
await this.storage.setItem<NodeSavedState>(key, val)
3023
}
3124

3225
async del() {
33-
const stateKey = getCookie(this.event, this.cookieKey)
34-
deleteCookie(this.event, this.cookieKey)
35-
if (stateKey) {
36-
await this.storage.del(stateKey)
37-
}
26+
await this.session.update({
27+
oauthState: undefined,
28+
})
3829
}
3930
}

server/utils/atproto/oauth.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { NodeOAuthClient } from '@atproto/oauth-client-node'
44
import { parse } from 'valibot'
55
import { getOAuthLock } from '#server/utils/atproto/lock'
66
import { useOAuthStorage } from '#server/utils/atproto/storage'
7-
import { UNSET_NUXT_SESSION_PASSWORD } from '#shared/utils/constants'
87
import { OAuthMetadataSchema } from '#shared/schemas/oauth'
98
// @ts-expect-error virtual file from oauth module
109
import { clientUri } from '#oauth/config'
10+
import { useServerSession } from '#server/utils/server-session'
1111
// TODO: limit scope as features gets added. atproto just allows login so no scary login screen till we have scopes
1212
export const scope = 'atproto'
1313

@@ -44,7 +44,8 @@ type EventHandlerWithOAuthSession<T extends EventHandlerRequest, D> = (
4444

4545
async function getOAuthSession(event: H3Event): Promise<OAuthSession | undefined> {
4646
const clientMetadata = getOauthClientMetadata()
47-
const { stateStore, sessionStore } = useOAuthStorage(event)
47+
const serverSession = await useServerSession(event)
48+
const { stateStore, sessionStore } = useOAuthStorage(serverSession)
4849

4950
const client = new NodeOAuthClient({
5051
stateStore,
@@ -64,18 +65,7 @@ export function eventHandlerWithOAuthSession<T extends EventHandlerRequest, D>(
6465
handler: EventHandlerWithOAuthSession<T, D>,
6566
) {
6667
return defineEventHandler(async event => {
67-
const config = useRuntimeConfig(event)
68-
69-
if (!config.sessionPassword) {
70-
throw createError({
71-
status: 500,
72-
message: UNSET_NUXT_SESSION_PASSWORD,
73-
})
74-
}
75-
76-
const serverSession = await useSession(event, {
77-
password: config.sessionPassword,
78-
})
68+
const serverSession = await useServerSession(event)
7969

8070
const oAuthSession = await getOAuthSession(event)
8171
return await handler(event, oAuthSession, serverSession)

server/utils/atproto/storage.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import type { H3Event } from 'h3'
1+
import type { SessionManager } from 'h3'
22
import { OAuthStateStore } from './oauth-state-store'
33
import { OAuthSessionStore } from './oauth-session-store'
4+
import type { UserServerSession } from '#shared/types/userSession'
45

5-
export const useOAuthStorage = (event: H3Event) => {
6+
export const useOAuthStorage = (session: SessionManager<UserServerSession>) => {
67
return {
7-
stateStore: new OAuthStateStore(event),
8-
sessionStore: new OAuthSessionStore(event),
8+
stateStore: new OAuthStateStore(session),
9+
sessionStore: new OAuthSessionStore(session),
910
}
1011
}

0 commit comments

Comments
 (0)