Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: moving cookie related logic to cookieAdapter.ts #42

Merged
Merged
Show file tree
Hide file tree
Changes from 12 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
801 changes: 0 additions & 801 deletions .yarn/releases/yarn-3.2.4.cjs

This file was deleted.

2 changes: 0 additions & 2 deletions .yarnrc

This file was deleted.

5 changes: 0 additions & 5 deletions .yarnrc.yml

This file was deleted.

6,255 changes: 2,787 additions & 3,468 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

33 changes: 21 additions & 12 deletions src/session-adapters/cookieAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expireCookie, getCookie, setCookie, verifyData } from '../utils'
import jwt from 'jsonwebtoken'
import { Adapter } from './publicAdapter'
import { isAppSession } from '../session'

const clientSecret = process.env['CLIENT_SECRET'] || ''

Expand All @@ -19,41 +20,49 @@ const createScopedKey = ({
// We do not use `clientId` in cookie adapter,
// because different plugins will have different domain names,
// and it's enough to differentiate these cookie values.

const sessionKey = 'sb.auth'
//NOTE: possibly cookieAdapter can become createCookieAdapter(key: string)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about this comment @eunjae-lee ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you agree with this I will also need to change a bit the docs (just a note for myself)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah agree with create... naming

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on it

Copy link
Contributor Author

@BibiSebi BibiSebi Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done ff31f67


export const cookieAdapter: Adapter = {
getItem: ({ req, spaceId, userId, key }) => {
const cookie = getCookie(req, createScopedKey({ spaceId, userId, key }))
getSession: ({ req, spaceId, userId }) => {
const cookie = getCookie(
req,
createScopedKey({ spaceId, userId, key: sessionKey }),
)

if (!cookie) {
return undefined
}

const verifiedData = verifyData(clientSecret, cookie)
if (!verifiedData) {

if (!isAppSession(verifiedData)) {
return undefined
} else {
return verifiedData as string
}

return verifiedData
},

setItem: ({ res, spaceId, userId, key, value }) => {
setSession: ({ res, spaceId, userId, session }) => {
const expires = new Date()
expires.setDate(expires.getDate() + 7)

const signedData = jwt.sign({ data: value }, clientSecret)
const signedData = jwt.sign({ data: session }, clientSecret)
setCookie(
res,
createScopedKey({ spaceId, userId, key }),
createScopedKey({ spaceId, userId, key: sessionKey }),
signedData,
expires,
)
return true
},

hasItem: async (params) =>
(await cookieAdapter.getItem(params)) !== undefined,
hasSession: async (params) =>
(await cookieAdapter.getSession(params)) !== undefined,

removeItem: ({ res, spaceId, userId, key }) => {
expireCookie(res, createScopedKey({ spaceId, userId, key }))
removeSession: ({ res, spaceId, userId }) => {
expireCookie(res, createScopedKey({ spaceId, userId, key: sessionKey }))
return true
},
}
80 changes: 45 additions & 35 deletions src/session-adapters/internalAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from '../utils'
import { AppSession } from '../session/types'
import { AuthHandlerParams } from '../storyblok-auth-api'
import { sessionIdentifier } from '../session/sessionIdentifier'

export type InternalAdapter = {
// session
Expand Down Expand Up @@ -52,7 +51,7 @@ type CreateInternalAdapter = ({
res,
adapter,
}: {
params: Pick<AuthHandlerParams, 'clientId' | 'clientSecret' | 'sessionKey'>
params: Pick<AuthHandlerParams, 'clientId' | 'clientSecret'>
req: http.IncomingMessage
res: http.ServerResponse
adapter: Adapter
Expand All @@ -64,65 +63,76 @@ export const createInternalAdapter: CreateInternalAdapter = ({
res,
adapter,
}) => {
const sessionKey = sessionIdentifier(params.sessionKey)

return {
getSession: async ({ spaceId, userId }) => {
const session = await adapter.getItem({
req,
res,
clientId: params.clientId,
spaceId,
userId,
key: sessionKey,
})
if (!session) {
return undefined
}
try {
return JSON.parse(session) as AppSession
} catch (err) {
const session = await adapter.getSession({
req,
res,
clientId: params.clientId,
spaceId,
userId,
})

if (!session) {
return undefined
}

return session
} catch (e) {
console.log('Retrieving the session failed: ', e)
return undefined
}
},

setSession: async ({ spaceId, userId, session }) => {
try {
return await adapter.setItem({
const isSessionSet = await adapter.setSession({
req,
res,
clientId: params.clientId,
spaceId,
userId,
key: sessionKey,
value: JSON.stringify(session),
session,
})
} catch (err) {

return isSessionSet
} catch (e) {
console.log('Setting the session failed: ', e)
return false
}
},

hasSession: ({ spaceId, userId }) =>
adapter.hasItem({
req,
res,
clientId: params.clientId,
spaceId,
userId,
key: sessionKey,
}),
hasSession: async ({ spaceId, userId }) => {
try {
const hasSession = await adapter.hasSession({
req,
res,
clientId: params.clientId,
spaceId,
userId,
})

return hasSession
} catch (e) {
console.log('Session could not be found: ', e)
return false
}
},

removeSession: async ({ spaceId, userId }) => {
try {
return await adapter.removeItem({
const sessionRemoved = await adapter.removeSession({
req,
res,
clientId: params.clientId,
spaceId,
userId,
key: sessionKey,
})
} catch (err) {

return sessionRemoved
} catch (e) {
console.log('Removing the session failed: ', e)
return false
}
},
Expand All @@ -133,15 +143,15 @@ export const createInternalAdapter: CreateInternalAdapter = ({
return true
},

getCallbackData() {
getCallbackData: () => {
const cookie = getCookie(req, callbackCookieName)
const data = verifyData(params.clientSecret, cookie || '') as
| CallbackCookieData
| undefined
return data
},

removeCallbackData() {
removeCallbackData: () => {
expireCookie(res, callbackCookieName)
return true
},
Expand Down
59 changes: 25 additions & 34 deletions src/session-adapters/publicAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,33 @@
import { IncomingMessage, ServerResponse } from 'node:http'
import { AppSession } from '../session'

export type MaybePromise<T> = T | Promise<T>

export type Adapter = {
getItem: (params: {
req: IncomingMessage
res: ServerResponse
clientId: string
spaceId: string
userId: string
key: string
}) => MaybePromise<string | undefined>
getSession: GetSession
setSession: SetSession
removeSession: RemoveSession
hasSession: HasSession
}

setItem: (params: {
req: IncomingMessage
res: ServerResponse
clientId: string
spaceId: string
userId: string
key: string
value: string
}) => MaybePromise<boolean>
type BaseSessionParams = {
req: IncomingMessage
res: ServerResponse
clientId: string
spaceId: string
userId: string
}

removeItem: (params: {
req: IncomingMessage
res: ServerResponse
clientId: string
spaceId: string
userId: string
key: string
}) => MaybePromise<boolean>
type GetSession = (
params: BaseSessionParams,
) => MaybePromise<AppSession | undefined>

hasItem: (params: {
req: IncomingMessage
res: ServerResponse
clientId: string
spaceId: string
userId: string
key: string
}) => MaybePromise<boolean>
}
type SetSession = (
params: BaseSessionParams & {
session: AppSession
},
) => MaybePromise<boolean>

type RemoveSession = (params: BaseSessionParams) => MaybePromise<boolean>

type HasSession = (params: BaseSessionParams) => MaybePromise<boolean>
44 changes: 27 additions & 17 deletions src/session/sessionStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { refreshStoredAppSession } from './refreshStoredAppSession'
import { cookieAdapter } from '../session-adapters/cookieAdapter'
import { createInternalAdapter } from '../session-adapters/internalAdapter'
import { IncomingMessage } from 'http'
import { getQueryFromUrl } from '../utils/query-params/get-query-string'

export const getSessionStore: AppSessionCookieStoreFactory =
(params) =>
Expand Down Expand Up @@ -46,27 +47,36 @@ export const inferSessionQuery = (
if (!req.url) {
return
}
const url = new URL(req.url)
BibiSebi marked this conversation as resolved.
Show resolved Hide resolved
const spaceId = url.searchParams.get('space_id')
const userId = url.searchParams.get('user_id')
if (spaceId && userId) {
return {
spaceId,
userId,
}

const sessionFromUrl = getSessionFromUrl(req.url)

if (sessionFromUrl) {
return sessionFromUrl
}

if (req.headers.referer) {
const refererUrl = new URL(req.url)
BibiSebi marked this conversation as resolved.
Show resolved Hide resolved
const spaceId = refererUrl.searchParams.get('space_id')
const userId = refererUrl.searchParams.get('user_id')
if (spaceId && userId) {
return {
spaceId,
userId,
}
}
return getSessionFromUrl(req.headers.referer)
}

return undefined
}

const getSessionFromUrl = (url: string): AppSessionQuery | undefined => {
const query = getQueryFromUrl(url)

if (!query) {
return undefined
}

const spaceId = query.get('space_id')
const userId = query.get('user_id')

if (!spaceId || !userId) {
return undefined
}

return {
spaceId,
userId,
}
}
1 change: 0 additions & 1 deletion src/storyblok-auth-api/ResponseElement.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/**
* This object describes an HTTP response in a framework-agnostic way.
* The idea is to create one function that parses an incoming HTTP message into a `ResponseElement`.
* This `ResponseElement` is then fed into a _reconciler_ that writes the required changes into an HTTP response.
*/
export type ResponseElement = {
type: 'success' | 'error' | 'configuration-error'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const changedCookieHeaderValue = (
return [
`${name}=${value}`,
'path=/',
expires ? `Expires=${expires.toISOString()}; ` : undefined,
expires ? `Expires=${expires}; ` : undefined,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eunjae-lee I changes this because it did not work for me and the expires attribute was set to Session, now it seems to work. Please could you double-check?

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I've just confirmed it!

'samesite=none',
'secure',
'httponly',
Expand Down
Loading