diff --git a/apps/examples/nextjs/.env.local.example b/apps/examples/nextjs/.env.local.example
index b038ef2eab..b754b2f5a7 100644
--- a/apps/examples/nextjs/.env.local.example
+++ b/apps/examples/nextjs/.env.local.example
@@ -16,4 +16,6 @@ AUTH_GOOGLE_SECRET=
 AUTH_TWITTER_ID=
 AUTH_TWITTER_SECRET=
 
+# THIRD_PARTY_API_EXAMPLE_BACKEND= # Read more at https://authjs.dev/guides/integrating-third-party-backends
+
 # AUTH_TRUST_HOST=1 # Read more at https://authjs.dev/getting-started/deployment#auth_trust_host
\ No newline at end of file
diff --git a/apps/examples/nextjs/app/[...proxy]/route.tsx b/apps/examples/nextjs/app/[...proxy]/route.tsx
new file mode 100644
index 0000000000..ae20cd18da
--- /dev/null
+++ b/apps/examples/nextjs/app/[...proxy]/route.tsx
@@ -0,0 +1,34 @@
+import { auth } from "@/auth"
+import { NextRequest } from "next/server"
+
+// Review if we need this, and why
+function stripContentEncoding(result: Response) {
+  const responseHeaders = new Headers(result.headers)
+  responseHeaders.delete("content-encoding")
+
+  return new Response(result.body, {
+    status: result.status,
+    statusText: result.statusText,
+    headers: responseHeaders,
+  })
+}
+
+export async function handler(request: NextRequest) {
+  const session = await auth()
+
+  const headers = new Headers(request.headers)
+  headers.set("Authorization", `Bearer ${session?.accessToken}`)
+
+  let backendUrl =
+    process.env.THIRD_PARTY_API_EXAMPLE_BACKEND ??
+    "https://authjs-third-party-backend.authjs.dev/"
+
+  let url = request.nextUrl.href.replace(request.nextUrl.origin, backendUrl)
+  let result = await fetch(url, { headers, body: request.body })
+  console.log("fetched", result)
+  return stripContentEncoding(result)
+}
+
+export const dynamic = "force-dynamic"
+
+export { handler as GET, handler as POST }
diff --git a/apps/examples/nextjs/auth.ts b/apps/examples/nextjs/auth.ts
index 058baed467..6d465e3bcd 100644
--- a/apps/examples/nextjs/auth.ts
+++ b/apps/examples/nextjs/auth.ts
@@ -1,4 +1,5 @@
 import NextAuth from "next-auth"
+import "next-auth/jwt"
 
 import Apple from "next-auth/providers/apple"
 import Auth0 from "next-auth/providers/auth0"
@@ -76,11 +77,30 @@ export const config = {
       if (pathname === "/middleware-example") return !!auth
       return true
     },
-    jwt({ token, trigger, session }) {
+    jwt({ token, trigger, session, account }) {
       if (trigger === "update") token.name = session.user.name
+      if (account?.provider === "keycloak") {
+        return { ...token, accessToken: account.access_token }
+      }
       return token
     },
+    async session({ session, token }) {
+      session.accessToken = token.accessToken
+      return session
+    },
   },
 } satisfies NextAuthConfig
 
 export const { handlers, auth, signIn, signOut } = NextAuth(config)
+
+declare module "next-auth" {
+  interface Session {
+    accessToken?: string
+  }
+}
+
+declare module "next-auth/jwt" {
+  interface JWT {
+    accessToken?: string
+  }
+}
diff --git a/apps/examples/nextjs/components/client-example.tsx b/apps/examples/nextjs/components/client-example.tsx
index 686ccd2d86..a1610451c3 100644
--- a/apps/examples/nextjs/components/client-example.tsx
+++ b/apps/examples/nextjs/components/client-example.tsx
@@ -43,6 +43,17 @@ const UpdateForm = () => {
 
 export default function ClientExample() {
   const { data: session, status } = useSession()
+  const [apiResponse, setApiResponse] = useState("")
+
+  const makeRequestWithToken = async () => {
+    try {
+      const response = await fetch("/api/authenticated/greeting")
+      const data = await response.json()
+      setApiResponse(JSON.stringify(data, null, 2))
+    } catch (error) {
+      setApiResponse("Failed to fetch data: " + error)
+    }
+  }
 
   return (
     <div className="flex flex-col gap-4">
@@ -71,6 +82,34 @@ export default function ClientExample() {
         to provide the session data.
       </p>
 
+      <div>
+        <h2 className="text-xl font-bold">Third-party backend integration</h2>
+        <p>
+          Press the button below to send a request to our{" "}
+          <CustomLink href="https://github.com/nextauthjs/authjs-third-party-backend">
+            <code>example backend</code>
+          </CustomLink>
+          .
+        </p>
+        <div className="flex flex-col ">
+          <p>Note: This example only works when using the Keycloak provider.</p>
+          <Button
+            disabled={!session?.accessToken}
+            className="mt-4 mb-4"
+            onClick={makeRequestWithToken}
+          >
+            Make API Request
+          </Button>
+        </div>
+        <p>
+          Read more{" "}
+          <CustomLink href="https://authjs.dev/guides/integrating-third-party-backends">
+            <code>here</code>
+          </CustomLink>
+        </p>
+        <pre>{apiResponse}</pre>
+      </div>
+
       {status === "loading" ? (
         <div>Loading...</div>
       ) : (
diff --git a/docs/pages/guides/integrating-third-party-backends.mdx b/docs/pages/guides/integrating-third-party-backends.mdx
new file mode 100644
index 0000000000..4f4f619f43
--- /dev/null
+++ b/docs/pages/guides/integrating-third-party-backends.mdx
@@ -0,0 +1,77 @@
+# Integrating with third-party backends
+
+When logging in through a provider, you can use the received OAuth tokens to authenticate against a third-party API.
+These tokens can be used to authorize requests to backends that are supporting the corresponding provider.
+
+For example:
+
+- GitHub's `access_token` will give you access to GitHub's APIs.
+- Self-managed providers (like Keycloak) can be used to authorize against custom third-party backends.
+
+## Storing the token in the session
+
+The token(s) are made availale in the `account` parameter of the jwt callback.
+To store them in the session, they can be attached to the token first.
+
+```typescript
+jwt({ token, trigger, session, account }) {
+  if (account?.provider === "my-provider") {
+    return { ...token, accessToken: account.access_token }
+  }
+  // ...
+}
+```
+
+In order to access the token when making API requests, it needs to be made available to the Auth.js session.
+
+```typescript
+async session({ session, token }) {
+  session.accessToken = token.accessToken
+  return session
+}
+```
+
+## Using the token to make authorized API requests
+
+OAuth tokens are commonly attached as `Authorization: Bearer <>` header.
+It is recommended to attach this header server side, like a [Route Handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers).
+
+```typescript
+export async function handler(request: NextRequest) {
+  const session = await auth()
+  return await fetch(/*<your-backend-url>/api/authenticated/greeting*/, {
+    headers: { "Authorization":  `Bearer ${session?.accessToken}` }
+  })
+  // ...
+}
+```
+
+## Configuring the backend to authorize requests through your provider
+
+Consult your backend framework's documentation on how to verify incoming access tokens.
+Below is an [example](https://github.com/nextauthjs/authjs-third-party-backend/tree/main/backend-express) with Express.js using a [Keycloak](https://providers.authjs.dev/keycloak) instance.
+
+```javascript
+const app = express()
+const jwtCheck = jwt({
+  secret: jwks.expressJwtSecret({
+    cache: true,
+    rateLimit: true,
+    jwksRequestsPerMinute: 5,
+    jwksUri:
+      "https://keycloak.authjs.dev/realms/master/protocol/openid-connect/certs",
+  }),
+  issuer: "https://keycloak.authjs.dev/realms/master",
+  algorithms: ["RS256"],
+})
+app.get("*", jwtCheck, (req, res) => {
+  const name = req.auth?.name ?? "unknown name"
+  res.json({ greeting: `Hello, ${name}!` })
+})
+// ...
+```
+
+## Resources
+
+- Further examples for different backend frameworks can be found [here](https://github.com/nextauthjs/authjs-third-party-backend/tree/main).
+- A full example of how to integrate a client app with a third-party API can be found in the [next-auth-example](https://github.com/nextauthjs/next-auth-example).