Skip to content

Commit

Permalink
implement cookies.unified option to use unified cookie
Browse files Browse the repository at this point in the history
a single unified cookie is required in order to make server side
authentication work in firebase hosting scenarions. since firebase
hosting only allows you to use on single cookie; cookies.signed
will default to false when cookies.unified is set to true

- related to github.com/gladly-team/issues/190
  • Loading branch information
maerzhase committed May 31, 2021
1 parent 333d538 commit 2ac7334
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 38 deletions.
3 changes: 3 additions & 0 deletions src/authCookies.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { getConfig } from 'src/config'

const getBaseCookieName = () => getConfig().cookies.name
const isCookieUnified = () => getConfig().cookies.unified

export const getAuthUserCookieName = () => {
const baseAuthCookieName = getBaseCookieName()
if (isCookieUnified()) return baseAuthCookieName
return `${baseAuthCookieName}.AuthUser`
}

export const getAuthUserTokensCookieName = () => {
const baseAuthCookieName = getBaseCookieName()
if (isCookieUnified()) return baseAuthCookieName
return `${baseAuthCookieName}.AuthUserTokens`
}
2 changes: 2 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const defaultConfig = {
sameSite: 'strict',
secure: true,
signed: true,
unified: false,
},
}

Expand Down Expand Up @@ -185,6 +186,7 @@ export const setConfig = (userConfig = {}) => {
cookies: {
...defaultConfig.cookies,
...cookieOptions,
signed: cookieOptions.unified ? false : cookieOptions.signed
},
}
const { isValid, errors } = validateConfig(mergedConfig)
Expand Down
81 changes: 46 additions & 35 deletions src/setAuthCookies.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const setAuthCookies = async (req, res) => {
token
)

const { unified, name } = getConfig().cookies;

// Pick a subset of the config.cookies options to
// pass to setCookie.
const cookieOptions = (({
Expand All @@ -45,43 +47,52 @@ const setAuthCookies = async (req, res) => {
signed,
}))(getConfig().cookies)

// Store the ID and refresh tokens in a cookie. This
// cookie will be available to future requests to pages,
// providing a valid Firebase ID token (refreshed as needed)
// for server-side rendering.
setCookie(
getAuthUserTokensCookieName(),
// Note: any change to cookie data structure needs to be
// backwards-compatible.
JSON.stringify({
idToken,
refreshToken,
}),
{ req, res },
cookieOptions
)
if(unified) {
setCookie(
name,
`${JSON.stringify({idToken, refreshToken})}|-|${AuthUser.serialize({ includeToken: false })}`,
{req, res},
cookieOptions
)
} else {

// Store the AuthUser data. This cookie will be available
// to future requests to pages, providing the user data. It
// will *not* include a Firebase ID token, because it may have
// expired, but provides the AuthUser data without any
// additional server-side requests.
setCookie(
getAuthUserCookieName(),
// Note: any change to cookie data structure needs to be
// backwards-compatible.
// Don't include the token in the "AuthUser" cookie, because
// the token should only be used from the "AuthUserTokens"
// cookie. Here, it is redundant information, and we don't
// want the token to be used if it's expired.
AuthUser.serialize({ includeToken: false }),
{
req,
res,
},
cookieOptions
)
// Store the ID and refresh tokens in a cookie. This
// cookie will be available to future requests to pages,
// providing a valid Firebase ID token (refreshed as needed)
// for server-side rendering.
setCookie(
getAuthUserTokensCookieName(),
// Note: any change to cookie data structure needs to be
// backwards-compatible.
JSON.stringify({
idToken,
refreshToken,
}),
{ req, res },
cookieOptions
)

// Store the AuthUser data. This cookie will be available
// to future requests to pages, providing the user data. It
// will *not* include a Firebase ID token, because it may have
// expired, but provides the AuthUser data without any
// additional server-side requests.
setCookie(
getAuthUserCookieName(),
// Note: any change to cookie data structure needs to be
// backwards-compatible.
// Don't include the token in the "AuthUser" cookie, because
// the token should only be used from the "AuthUserTokens"
// cookie. Here, it is redundant information, and we don't
// want the token to be used if it's expired.
AuthUser.serialize({ includeToken: false }),
{
req,
res,
},
cookieOptions
)
}
return {
idToken,
refreshToken,
Expand Down
8 changes: 5 additions & 3 deletions src/withAuthUserTokenSSR.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const withAuthUserTokenSSR = (
) => (getServerSidePropsFunc) => async (ctx) => {
const { req, res } = ctx

const { keys, secure, signed } = getConfig().cookies
const { keys, secure, signed, unified } = getConfig().cookies

let AuthUser

Expand All @@ -55,14 +55,15 @@ const withAuthUserTokenSSR = (
if (useToken) {
// Get the user's ID token from a cookie, verify it (refreshing
// as needed), and return the serialized AuthUser in props.
const cookieValStr = getCookie(
let cookieValStr = getCookie(
getAuthUserTokensCookieName(),
{
req,
res,
},
{ keys, secure, signed }
)
if (unified && cookieValStr) cookieValStr = cookieValStr.split('|-|')[0];
const { idToken, refreshToken } = cookieValStr
? JSON.parse(cookieValStr)
: {}
Expand All @@ -74,14 +75,15 @@ const withAuthUserTokenSSR = (
} else {
// Get the user's info from a cookie, verify it (refreshing
// as needed), and return the serialized AuthUser in props.
const cookieValStr = getCookie(
let cookieValStr = getCookie(
getAuthUserCookieName(),
{
req,
res,
},
{ keys, secure, signed }
)
if (unified && cookieValStr) cookieValStr = cookieValStr.split('|-|')[1];
AuthUser = createAuthUser({
serializedAuthUser: cookieValStr,
})
Expand Down

0 comments on commit 2ac7334

Please sign in to comment.