Skip to content

Commit

Permalink
feat(core): allow clearing cookies from jwt() (#6337)
Browse files Browse the repository at this point in the history
* feat(core): allow clearing cookies from `jwt()`

* revert: allow clearing cookies from `jwt()`

* feat(core): re-apply changes against `@auth/core`

* revert: `decodeJWT` option

* doc: `jwt()` callback

* Apply suggestions from code review

Co-authored-by: Balázs Orbán <[email protected]>
  • Loading branch information
iMrDJAi and balazsorban44 authored Jan 10, 2023
1 parent 2377596 commit 6d4cde4
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 48 deletions.
77 changes: 46 additions & 31 deletions packages/core/src/lib/routes/callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,22 @@ export async function callback(params: {
isNewUser,
})

// Encode token
const newToken = await jwt.encode({ ...jwt, token })

// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)

const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
cookies.push(...sessionCookies)
// Clear cookies if token is null
if (token === null) {
cookies.push(...sessionStore.clean())
} else {
// Encode token
const newToken = await jwt.encode({ ...jwt, token })

// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)

const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
cookies.push(...sessionCookies)
}
} else {
// Save Session Token in cookie
cookies.push({
Expand Down Expand Up @@ -214,17 +219,22 @@ export async function callback(params: {
isNewUser,
})

// Encode token
const newToken = await jwt.encode({ ...jwt, token })

// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)

const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
cookies.push(...sessionCookies)
// Clear cookies if token is null
if (token === null) {
cookies.push(...sessionStore.clean())
} else {
// Encode token
const newToken = await jwt.encode({ ...jwt, token })

// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)

const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
cookies.push(...sessionCookies)
}
} else {
// Save Session Token in cookie
cookies.push({
Expand Down Expand Up @@ -305,18 +315,23 @@ export async function callback(params: {
isNewUser: false,
})

// Encode token
const newToken = await jwt.encode({ ...jwt, token })
// Clear cookies if token is null
if (token === null) {
cookies.push(...sessionStore.clean())
} else {
// Encode token
const newToken = await jwt.encode({ ...jwt, token })

// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)
// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)

const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})

cookies.push(...sessionCookies)
cookies.push(...sessionCookies)
}

// @ts-expect-error
await events.signIn?.({ user, account })
Expand Down
37 changes: 21 additions & 16 deletions packages/core/src/lib/routes/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,27 +48,32 @@ export async function session(

// @ts-expect-error
const token = await callbacks.jwt({ token: decodedToken })
// @ts-expect-error
const newSession = await callbacks.session({ session, token })

// Return session payload as response
response.body = newSession
if (token !== null) {
// @ts-expect-error
const newSession = await callbacks.session({ session, token })

// Refresh JWT expiry by re-signing it, with an updated expiry date
const newToken = await jwt.encode({
...jwt,
token,
maxAge: options.session.maxAge,
})
// Return session payload as response
response.body = newSession

// Set cookie, to also update expiry date on cookie
const sessionCookies = sessionStore.chunk(newToken, {
expires: newExpires,
})
// Refresh JWT expiry by re-signing it, with an updated expiry date
const newToken = await jwt.encode({
...jwt,
token,
maxAge: options.session.maxAge,
})

// Set cookie, to also update expiry date on cookie
const sessionCookies = sessionStore.chunk(newToken, {
expires: newExpires,
})

response.cookies?.push(...sessionCookies)
response.cookies?.push(...sessionCookies)

await events.session?.({ session: newSession, token })
await events.session?.({ session: newSession, token })
} else {
response.cookies?.push(...sessionStore.clean())
}
} catch (e) {
logger.error(new JWTSessionError(e as Error))
// If the JWT is not verifiable remove the broken session cookie(s).
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ export interface CallbacksOptions<P = Profile, A = Account> {
* Its content is forwarded to the `session` callback,
* where you can control what should be returned to the client.
* Anything else will be kept inaccessible from the client.
*
* Returning `null` will invalidate the JWT session by clearing
* the user's cookies. You'll still have to monitor and invalidate
* unexpired tokens from future requests yourself to prevent
* unauthorized access.
*
* By default the JWT is encrypted.
*
Expand All @@ -215,7 +220,7 @@ export interface CallbacksOptions<P = Profile, A = Account> {
account?: A | null
profile?: P
isNewUser?: boolean
}) => Awaitable<JWT>
}) => Awaitable<JWT|null>
}

/** [Documentation](https://authjs.dev/reference/configuration/auth-config#cookies) */
Expand Down

1 comment on commit 6d4cde4

@vercel
Copy link

@vercel vercel bot commented on 6d4cde4 Jan 10, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.