Skip to content

Commit 0d62479

Browse files
committed
ServerAuthState types and stuff
Inject initial server auth state on ssr
1 parent d25ed51 commit 0d62479

File tree

6 files changed

+68
-16
lines changed

6 files changed

+68
-16
lines changed

packages/auth/ambient.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ declare global {
2222
__REDWOOD__APP_TITLE: string
2323
}
2424

25+
var __REDWOOD__SERVER__AUTH_STATE__: AuthProviderState<any>
26+
2527
namespace NodeJS {
2628
interface Global {
2729
/** URL or absolute path to the GraphQL serverless function */
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,61 @@
1+
import type { ReactNode } from 'react'
12
import React from 'react'
23

34
import type { AuthProviderState } from './AuthProviderState'
45
import { defaultAuthProviderState } from './AuthProviderState'
56

6-
export const ServerAuthContext = React.createContext<
7-
AuthProviderState<never> & {
8-
encryptedSession: string | null
9-
cookieHeader?: string
7+
export type ServerAuthState = AuthProviderState<never> & {
8+
// Used by AuthProvider in getToken. We can probably remove this
9+
encryptedSession?: string | null
10+
cookieHeader?: string
11+
}
12+
13+
/**
14+
* On the server, it resolve to the defaultAuthProviderState first
15+
*/
16+
export const ServerAuthContext = React.createContext<ServerAuthState>(
17+
globalThis?.__REDWOOD__SERVER__AUTH_STATE__ || {
18+
...defaultAuthProviderState,
19+
encryptedSession: null,
1020
}
11-
>({ ...defaultAuthProviderState, encryptedSession: null })
21+
)
22+
23+
/***
24+
* Note: This only gets rendered on the server and serves two purposes:
25+
* 1) On the server, it sets the auth state
26+
* 2) On the client, it restores the auth state from the initial server render
27+
*/
28+
export const ServerAuthProvider = ({
29+
value,
30+
children,
31+
}: {
32+
value: ServerAuthState
33+
children?: ReactNode[]
34+
}) => {
35+
// @NOTE: we "Sanitize" to remove encryptedSession and cookieHeader
36+
// not totally necessary, but it's nice to not have them in the DOM
37+
// @MARK: needs discussion!
38+
const stringifiedAuthState = `__REDWOOD__SERVER__AUTH_STATE__ = ${JSON.stringify(
39+
sanitizeServerAuthState(value)
40+
)};`
41+
42+
return (
43+
<>
44+
<script
45+
id="__REDWOOD__SERVER_AUTH_STATE__"
46+
dangerouslySetInnerHTML={{
47+
__html: stringifiedAuthState,
48+
}}
49+
/>
1250

13-
export const ServerAuthProvider = ServerAuthContext.Provider
51+
<ServerAuthContext.Provider value={value}>
52+
{children}
53+
</ServerAuthContext.Provider>
54+
</>
55+
)
56+
}
57+
function sanitizeServerAuthState(value: ServerAuthState) {
58+
const sanitizedState = { ...value }
59+
delete sanitizedState.encryptedSession && delete sanitizedState.cookieHeader
60+
return sanitizedState
61+
}

packages/auth/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export { useNoAuth, UseAuth } from './useAuth'
33
export { createAuthentication } from './authFactory'
44
export type { AuthImplementation } from './AuthImplementation'
55

6+
export * from './AuthProvider/AuthProviderState'
67
export * from './AuthProvider/ServerAuthProvider'

packages/vite/src/streaming/createReactStreamingHandler.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import path from 'path'
33
import isbot from 'isbot'
44
import type { ViteDevServer } from 'vite'
55

6+
import { defaultAuthProviderState } from '@redwoodjs/auth'
67
import type { RWRouteManifestItem } from '@redwoodjs/internal'
78
import { getAppRouteHook, getConfig, getPaths } from '@redwoodjs/project-config'
89
import { matchPath } from '@redwoodjs/router'
@@ -68,7 +69,7 @@ export const createReactStreamingHandler = async (
6869
})
6970
}
7071

71-
let decodedAuthState
72+
let decodedAuthState = defaultAuthProviderState
7273

7374
// Do this inside the handler for **dev-only**.
7475
// This makes sure that changes to entry-server are picked up on refresh

packages/vite/src/streaming/streamHelpers.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import type {
77
ReactDOMServerReadableStream,
88
} from 'react-dom/server'
99

10+
import type { ServerAuthState } from '@redwoodjs/auth'
1011
import { ServerAuthProvider } from '@redwoodjs/auth'
11-
import type { AuthProviderState } from '@redwoodjs/auth/src/AuthProvider/AuthProviderState'
1212
import { LocationProvider } from '@redwoodjs/router'
1313
import type { TagDescriptor } from '@redwoodjs/web'
1414
// @TODO (ESM), use exports field. Cannot import from web because of index exports
@@ -29,7 +29,7 @@ interface RenderToStreamArgs {
2929
cssLinks: string[]
3030
isProd: boolean
3131
jsBundles?: string[]
32-
authState: AuthProviderState<never> & { token: string | null }
32+
authState: ServerAuthState
3333
}
3434

3535
interface StreamOptions {
@@ -92,8 +92,7 @@ export async function reactRenderToStreamResponse(
9292
return React.createElement(
9393
ServerAuthProvider,
9494
{
95-
// @TODO: figure out types
96-
value: authState as any,
95+
value: authState,
9796
},
9897
React.createElement(
9998
LocationProvider,
@@ -144,11 +143,10 @@ export async function reactRenderToStreamResponse(
144143
},
145144
}
146145

146+
const root = renderRoot(currentPathName)
147+
147148
const reactStream: ReactDOMServerReadableStream =
148-
await renderToReadableStream(
149-
renderRoot(currentPathName),
150-
renderToStreamOptions
151-
)
149+
await renderToReadableStream(root, renderToStreamOptions)
152150

153151
// @NOTE: very important that we await this before we apply any transforms
154152
if (waitForAllReady) {

packages/web/src/apollo/suspense.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ const ApolloProviderWithFetchConfig: React.FunctionComponent<{
122122

123123
const { uri } = useFetchConfig()
124124

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

127128
const getGraphqlUrl = () => {
@@ -147,7 +148,8 @@ const ApolloProviderWithFetchConfig: React.FunctionComponent<{
147148
link: createHttpLink(
148149
getGraphqlUrl(),
149150
httpLinkConfig,
150-
serverAuthState.cookieHeader
151+
// @TODO how is serverAuthState not the default
152+
serverAuthState?.cookieHeader
151153
),
152154
},
153155
]

0 commit comments

Comments
 (0)