Skip to content

Commit

Permalink
ServerAuthState types and stuff
Browse files Browse the repository at this point in the history
Inject initial server auth state on ssr
  • Loading branch information
dac09 committed Jan 4, 2024
1 parent d25ed51 commit 0d62479
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 16 deletions.
2 changes: 2 additions & 0 deletions packages/auth/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ declare global {
__REDWOOD__APP_TITLE: string
}

var __REDWOOD__SERVER__AUTH_STATE__: AuthProviderState<any>

namespace NodeJS {
interface Global {
/** URL or absolute path to the GraphQL serverless function */
Expand Down
60 changes: 54 additions & 6 deletions packages/auth/src/AuthProvider/ServerAuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,61 @@
import type { ReactNode } from 'react'
import React from 'react'

import type { AuthProviderState } from './AuthProviderState'
import { defaultAuthProviderState } from './AuthProviderState'

export const ServerAuthContext = React.createContext<
AuthProviderState<never> & {
encryptedSession: string | null
cookieHeader?: string
export type ServerAuthState = AuthProviderState<never> & {
// Used by AuthProvider in getToken. We can probably remove this
encryptedSession?: string | null
cookieHeader?: string
}

/**
* On the server, it resolve to the defaultAuthProviderState first
*/
export const ServerAuthContext = React.createContext<ServerAuthState>(
globalThis?.__REDWOOD__SERVER__AUTH_STATE__ || {
...defaultAuthProviderState,
encryptedSession: null,
}
>({ ...defaultAuthProviderState, encryptedSession: null })
)

/***
* Note: This only gets rendered on the server and serves two purposes:
* 1) On the server, it sets the auth state
* 2) On the client, it restores the auth state from the initial server render
*/
export const ServerAuthProvider = ({
value,
children,
}: {
value: ServerAuthState
children?: ReactNode[]
}) => {
// @NOTE: we "Sanitize" to remove encryptedSession and cookieHeader
// not totally necessary, but it's nice to not have them in the DOM
// @MARK: needs discussion!
const stringifiedAuthState = `__REDWOOD__SERVER__AUTH_STATE__ = ${JSON.stringify(
sanitizeServerAuthState(value)
)};`

return (
<>
<script
id="__REDWOOD__SERVER_AUTH_STATE__"
dangerouslySetInnerHTML={{
__html: stringifiedAuthState,
}}
/>

export const ServerAuthProvider = ServerAuthContext.Provider
<ServerAuthContext.Provider value={value}>
{children}
</ServerAuthContext.Provider>
</>
)
}
function sanitizeServerAuthState(value: ServerAuthState) {
const sanitizedState = { ...value }
delete sanitizedState.encryptedSession && delete sanitizedState.cookieHeader
return sanitizedState
}
1 change: 1 addition & 0 deletions packages/auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export { useNoAuth, UseAuth } from './useAuth'
export { createAuthentication } from './authFactory'
export type { AuthImplementation } from './AuthImplementation'

export * from './AuthProvider/AuthProviderState'
export * from './AuthProvider/ServerAuthProvider'
3 changes: 2 additions & 1 deletion packages/vite/src/streaming/createReactStreamingHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from 'path'
import isbot from 'isbot'
import type { ViteDevServer } from 'vite'

import { defaultAuthProviderState } from '@redwoodjs/auth'
import type { RWRouteManifestItem } from '@redwoodjs/internal'
import { getAppRouteHook, getConfig, getPaths } from '@redwoodjs/project-config'
import { matchPath } from '@redwoodjs/router'
Expand Down Expand Up @@ -68,7 +69,7 @@ export const createReactStreamingHandler = async (
})
}

let decodedAuthState
let decodedAuthState = defaultAuthProviderState

// Do this inside the handler for **dev-only**.
// This makes sure that changes to entry-server are picked up on refresh
Expand Down
14 changes: 6 additions & 8 deletions packages/vite/src/streaming/streamHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import type {
ReactDOMServerReadableStream,
} from 'react-dom/server'

import type { ServerAuthState } from '@redwoodjs/auth'
import { ServerAuthProvider } from '@redwoodjs/auth'
import type { AuthProviderState } from '@redwoodjs/auth/src/AuthProvider/AuthProviderState'
import { LocationProvider } from '@redwoodjs/router'
import type { TagDescriptor } from '@redwoodjs/web'
// @TODO (ESM), use exports field. Cannot import from web because of index exports
Expand All @@ -29,7 +29,7 @@ interface RenderToStreamArgs {
cssLinks: string[]
isProd: boolean
jsBundles?: string[]
authState: AuthProviderState<never> & { token: string | null }
authState: ServerAuthState
}

interface StreamOptions {
Expand Down Expand Up @@ -92,8 +92,7 @@ export async function reactRenderToStreamResponse(
return React.createElement(
ServerAuthProvider,
{
// @TODO: figure out types
value: authState as any,
value: authState,
},
React.createElement(
LocationProvider,
Expand Down Expand Up @@ -144,11 +143,10 @@ export async function reactRenderToStreamResponse(
},
}

const root = renderRoot(currentPathName)

const reactStream: ReactDOMServerReadableStream =
await renderToReadableStream(
renderRoot(currentPathName),
renderToStreamOptions
)
await renderToReadableStream(root, renderToStreamOptions)

// @NOTE: very important that we await this before we apply any transforms
if (waitForAllReady) {
Expand Down
4 changes: 3 additions & 1 deletion packages/web/src/apollo/suspense.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const ApolloProviderWithFetchConfig: React.FunctionComponent<{

const { uri } = useFetchConfig()

// @MARK server auth state should never be undefined???
const serverAuthState = useContext(ServerAuthContext)

const getGraphqlUrl = () => {
Expand All @@ -147,7 +148,8 @@ const ApolloProviderWithFetchConfig: React.FunctionComponent<{
link: createHttpLink(
getGraphqlUrl(),
httpLinkConfig,
serverAuthState.cookieHeader
// @TODO how is serverAuthState not the default
serverAuthState?.cookieHeader
),
},
]
Expand Down

0 comments on commit 0d62479

Please sign in to comment.