Skip to content

Commit

Permalink
feat: add getServerSession and SessionProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-jonas committed Dec 9, 2024
1 parent df98c2b commit 55ba82c
Show file tree
Hide file tree
Showing 14 changed files with 231 additions and 26 deletions.
7 changes: 2 additions & 5 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@ const config = tseslint.config([
"!.storybook",
"**/dist/**",
"**/storybook-static/**",
"babel.config.js",
"eslint.config.mjs",
"**/*.config.{js,mjs,cjs,ts}",
"**/playwright-report/**",
"examples/nextjs-spa/**", // This project is not maintained
"**/playwright.config.{js,ts}",
"**/playwright-ct.config.ts",
"**/jest.config.{js,ts}",
"**/jest.preset.{cjs,ts}",
"playwright/**",
"**/tsup.config.{js,ts}",
"**/.next/**"
],
},
eslint.configs.recommended,
Expand Down
59 changes: 47 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/elements-react/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"use client"
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

export { useSession } from "./useSession"
export { SessionProvider, useSession } from "./session-provider"
99 changes: 99 additions & 0 deletions packages/elements-react/src/client/session-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"use client"
import { Session } from "@ory/client-fetch"
import {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from "react"
import { frontendClient } from "./frontendClient"

type SessionData =
| {
session: Session
state: "authenticated"
}
| {
state: "unauthenticated"
}
| {
state: "loading"
}
| {
state: "error"
error: Error
}

const SessionContext = createContext<SessionData>({
state: "loading",
})

export function useSession() {
if (!SessionContext) {
throw new Error("[Ory/Elements] useSession must be used on the client")
}
const session = useContext(SessionContext)
if (!session) {
throw new Error(
"[Ory/Elements] useSession must be used within a SessionProvider",
)
}
return session
}
type SessionProviderProps = {
session?: Session | null
/**
* The time in milliseconds after which the session should be considered stale.
* If the session is stale, the session will be refreshed.
* If set to Infinity, the session will never be refreshed.
*/
staleTime?: number
} & React.PropsWithChildren

export function SessionProvider({
session: initialSession,
staleTime = Infinity,
children,
}: SessionProviderProps) {
const [lastSync, setLastSync] = useState<number>(0)
const [session, setSession] = useState<SessionData>(() => {
if (initialSession) {
return {
session: initialSession,
state: initialSession.active ? "authenticated" : "unauthenticated",
}
}

return { state: "unauthenticated" }
})

const fetchSession = useCallback(async () => {
try {
const session = await frontendClient().toSession()
setSession({
session,
state: session.active ? "authenticated" : "unauthenticated",
})
setLastSync(Date.now())
} catch (error) {
setSession({ state: "error", error: error as Error })
}
}, [])

const isLoading = session.state === "loading"

useEffect(() => {
// If the session is already loaded or we're currently loading it, we don't need to fetch it again
if (lastSync + staleTime > Date.now() || isLoading) {
return
}
void fetchSession()
}, [fetchSession, lastSync, staleTime, isLoading])

return (
<SessionContext.Provider value={session}>
{children}
</SessionContext.Provider>
)
}
2 changes: 1 addition & 1 deletion packages/elements-react/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default defineConfig([
sourcemap: true,
bundle: false,
format: ["cjs", "esm"],
entry: ["src/client/**/*.ts"],
entry: ["src/client/**/*.{ts,tsx}"],
outDir: "dist/client",
},
{
Expand Down
3 changes: 1 addition & 2 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@
"dependencies": {
"@ory/client-fetch": "^1.15.6",
"cookie": "^1.0.1",
"psl": "^1.10.0",
"psl": "^1.15.0",
"set-cookie-parser": "^2.7.1"
},
"devDependencies": {
"@types/cookie": "^0.6.0",
"@types/psl": "^1.1.3",
"@types/set-cookie-parser": "^2.4.10",
"babel-jest": "^29.7.0",
"jest-esm-transformer": "^1.0.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/nextjs/src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ export { getLoginFlow } from "./login"
export { getRegistrationFlow } from "./registration"
export { getRecoveryFlow } from "./recovery"
export { getVerificationFlow } from "./verification"
export { getSettingsFlow } from "./settings"
export { getServerSession } from "./session"

export type { OryPageParams } from "./utils"
12 changes: 12 additions & 0 deletions packages/nextjs/src/app/session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Session } from "@ory/client-fetch"
import { serverSideFrontendClient } from "./client"
import { getCookieHeader } from "./utils"

export async function getServerSession(): Promise<Session | null> {
const cookie = await getCookieHeader()
return serverSideFrontendClient
.toSession({
cookie,
})
.catch(() => null)
}
53 changes: 53 additions & 0 deletions packages/nextjs/src/app/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { FlowType, SettingsFlow } from "@ory/client-fetch"

import { initOverrides, QueryParams } from "../types"
import { serverSideFrontendClient } from "./client"
import { getFlow } from "./flow"
import { toFlowParams } from "./utils"

/**
* Use this method in an app router page to fetch an existing login flow or to create a new one. This method works with server-side rendering.
*
* ```
* import { Login } from "@ory/elements-react/theme"
* import { getLoginFlow, OryPageParams } from "@ory/nextjs/app"
* import { enhanceConfig } from "@ory/nextjs"
*
* import config from "@/ory.config"
* import CardHeader from "@/app/auth/login/card-header"
*
* export default async function LoginPage(props: OryPageParams) {
* const flow = await getLoginFlow(props.searchParams)
*
* if (!flow) {
* return null
* }
*
* return (
* <Login
* flow={flow}
* config={enhanceConfig(config)}
* components={{
* Card: {
* Header: CardHeader,
* },
* }}
* />
* )
* }
* ```
*
* @param params - The query parameters of the request.
*/
export async function getSettingsFlow(
params: QueryParams | Promise<QueryParams>,
): Promise<SettingsFlow | null | void> {
const p = await toFlowParams(await params)
return getFlow(
await params,
() => serverSideFrontendClient.getSettingsFlowRaw(p, initOverrides),
FlowType.Settings,
)
}
2 changes: 1 addition & 1 deletion packages/nextjs/src/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ export async function getPublicUrl() {
}

export interface OryPageParams {
searchParams: Promise<URLSearchParams>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
7 changes: 6 additions & 1 deletion packages/nextjs/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,15 @@ export interface OryConfig {
* `/my-settings`.
*/
settingsUiPath?: string

/**
* Set this to use a custom default redirect URI. This path should be relative to your application's base URL.
*/
defaultRedirectUri?: string
}
}

export type QueryParams = { [key: string]: string }
export type QueryParams = { [key: string]: string | string[] | undefined }

export const initOverrides: RequestInit = {
cache: "no-cache",
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/src/utils/cookie.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

import { OryConfig } from "../types"
import { errorCodes, ErrorResult, parse } from "psl"
import { OryConfig } from "../types"

function isErrorResult(
result: unknown,
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/src/utils/rewrite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function rewriteUrls(
["/ui/login", config.override?.loginUiPath],
["/ui/verification", config.override?.verificationUiPath],
["/ui/settings", config.override?.settingsUiPath],
["/ui/welcome", config.override?.defaultRedirectUri],
].entries()) {
const match = joinUrlPaths(matchBaseUrl, matchPath || "")
if (replaceWith && source.startsWith(match)) {
Expand Down
5 changes: 3 additions & 2 deletions packages/nextjs/tsconfig.lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"outDir": "../../dist/out-tsc",
"declaration": true,
"types": ["node"],
"rootDir": "./src"
"rootDir": "./src",
"jsx": "react-jsx"
},
"include": ["src/**/*.ts"],
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
}

0 comments on commit 55ba82c

Please sign in to comment.