diff --git a/docs/_partials/authenticate-req.mdx b/docs/_partials/authenticate-req.mdx index b3135e8205..0777f2ffd3 100644 --- a/docs/_partials/authenticate-req.mdx +++ b/docs/_partials/authenticate-req.mdx @@ -2,17 +2,22 @@ import { createClerkClient } from '@clerk/backend' export async function GET(req: Request) { + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY, publishableKey: process.env.CLERK_PUBLISHABLE_KEY, }) - const { isSignedIn } = await clerkClient.authenticateRequest(req, { - jwtKey: process.env.CLERK_JWT_KEY, + // Use the `authenticateRequest()` method to verify the token + const { isAuthenticated } = await clerkClient.authenticateRequest(req, { authorizedParties: ['https://example.com'], + jwtKey: process.env.CLERK_JWT_KEY, }) - if (!isSignedIn) { + // Protect the route from unauthenticated users + if (!isAuthenticated) { return Response.json({ status: 401 }) } diff --git a/docs/_partials/backend/usage.mdx b/docs/_partials/backend/usage.mdx index 262c3136dd..6294086b10 100644 --- a/docs/_partials/backend/usage.mdx +++ b/docs/_partials/backend/usage.mdx @@ -1,2 +1,2 @@ > [!NOTE] -> Importing `clerkClient` varies based on your framework. Refer to the [JS Backend SDK overview](/docs/js-backend/getting-started/quickstart) for usage details, including guidance on [how to access the `userId` and other properties](/docs/js-backend/getting-started/quickstart#get-the-user-id-and-other-properties). +> Using `clerkClient` varies based on your framework. Refer to the [JS Backend SDK overview](/docs/js-backend/getting-started/quickstart) for usage details, including guidance on [how to access the `userId` and other properties](/docs/js-backend/getting-started/quickstart#get-the-user-id-and-other-properties). diff --git a/docs/_partials/create-invitation.mdx b/docs/_partials/create-invitation.mdx new file mode 100644 index 0000000000..c4951862e0 --- /dev/null +++ b/docs/_partials/create-invitation.mdx @@ -0,0 +1,123 @@ + + + ```ts {{ filename: 'app/api/example/route.ts' }} + import { clerkClient } from '@clerk/nextjs/server' + import { NextResponse } from 'next/server' + + export async function GET() { + try { + const client = await clerkClient() + const invitation = await client.invitations.createInvitation({ + emailAddress: 'invite@example.com', + redirectUrl: 'https://www.example.com/my-sign-up', + publicMetadata: { + example: 'metadata', + example_nested: { + nested: 'metadata', + }, + }, + }) + return NextResponse.json({ message: 'Invitation created', invitation }) + } catch (error) { + console.log(error) + return NextResponse.json({ error: 'Error creating invitation' }) + } + } + ``` + + + + ```tsx {{ filename: 'src/api/example.ts' }} + import type { APIRoute } from 'astro' + import { clerkClient } from '@clerk/astro/server' + + export const GET: APIRoute = async (context) => { + await clerkClient(context).invitations.createInvitation({ + emailAddress: 'invite@example.com', + redirectUrl: 'https://www.example.com/my-sign-up', + publicMetadata: { + example: 'metadata', + example_nested: { + nested: 'metadata', + }, + }, + }) + + return new Response(JSON.stringify({ success: true }), { status: 200 }) + } + ``` + + + + ```ts {{ filename: 'public.ts' }} + import { getAuth, clerkClient } from '@clerk/express' + + app.post('/createUser', async (req, res) => { + await clerkClient.invitations.createInvitation({ + emailAddress: 'invite@example.com', + redirectUrl: 'https://www.example.com/my-sign-up', + publicMetadata: { + example: 'metadata', + example_nested: { + nested: 'metadata', + }, + }, + password: 'password', + }) + + res.status(200).json({ success: true }) + }) + ``` + + + + ```tsx {{ filename: 'app/routes/example.tsx' }} + import { clerkClient } from '@clerk/react-router/server' + import type { Route } from './+types/example' + + export async function loader(args: Route.LoaderArgs) { + await clerkClient.invitations.createInvitation({ + emailAddress: 'invite@example.com', + redirectUrl: 'https://www.example.com/my-sign-up', + publicMetadata: { + example: 'metadata', + example_nested: { + nested: 'metadata', + }, + }, + }) + + return { success: true } + } + ``` + + + + ```tsx {{ filename: 'app/routes/api/example.tsx' }} + import { json } from '@tanstack/react-start' + import { createFileRoute } from '@tanstack/react-router' + import { clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + await clerkClient().invitations.createInvitation({ + emailAddress: 'invite@example.com', + redirectUrl: 'https://www.example.com/my-sign-up', + publicMetadata: { + example: 'metadata', + example_nested: { + nested: 'metadata', + }, + }, + }) + + return json({ success: true }) + }, + }, + }, + }) + ``` + + diff --git a/docs/_partials/create-user.mdx b/docs/_partials/create-user.mdx new file mode 100644 index 0000000000..9387b28b0d --- /dev/null +++ b/docs/_partials/create-user.mdx @@ -0,0 +1,92 @@ + + + ```ts {{ filename: 'app/api/example/route.ts' }} + import { auth, clerkClient } from '@clerk/nextjs/server' + import { NextResponse } from 'next/server' + + export async function GET() { + try { + const client = await clerkClient() + const user = await client.users.createUser({ + emailAddress: ['test@example.com'], + password: 'password', + }) + return NextResponse.json({ message: 'User created', user }) + } catch (error) { + console.log(error) + return NextResponse.json({ error: 'Error creating user' }) + } + } + ``` + + + + ```tsx {{ filename: 'src/api/example.ts' }} + import type { APIRoute } from 'astro' + import { clerkClient } from '@clerk/astro/server' + + export const GET: APIRoute = async (context) => { + await clerkClient(context).users.createUser({ + emailAddress: ['test@example.com'], + password: 'password', + }) + + return new Response(JSON.stringify({ success: true }), { status: 200 }) + } + ``` + + + + ```ts {{ filename: 'public.ts' }} + import { getAuth, clerkClient } from '@clerk/express' + + app.post('/createUser', async (req, res) => { + await clerkClient.users.createUser({ + emailAddress: ['test@example.com'], + password: 'password', + }) + + res.status(200).json({ success: true }) + }) + ``` + + + + ```tsx {{ filename: 'app/routes/example.tsx' }} + import { clerkClient, getAuth } from '@clerk/react-router/server' + import type { Route } from './+types/example' + + export async function loader(args: Route.LoaderArgs) { + await clerkClient.users.createUser({ + emailAddress: ['test@example.com'], + password: 'password', + }) + + return { success: true } + } + ``` + + + + ```tsx {{ filename: 'app/routes/api/example.tsx' }} + import { json } from '@tanstack/react-start' + import { createFileRoute } from '@tanstack/react-router' + import { clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + await clerkClient().users.createUser({ + emailAddress: ['test@example.com'], + password: 'my-secure-password', + }) + + return json({ success: true }) + }, + }, + }, + }) + ``` + + diff --git a/docs/_partials/delete-user.mdx b/docs/_partials/delete-user.mdx new file mode 100644 index 0000000000..a9f9bf552f --- /dev/null +++ b/docs/_partials/delete-user.mdx @@ -0,0 +1,82 @@ + + + ```ts {{ filename: 'app/api/example/route.ts' }} + import { auth, clerkClient } from '@clerk/nextjs/server' + import { NextResponse } from 'next/server' + + export async function GET() { + const { userId } = await auth() + + await clerkClient.users.deleteUser(userId) + + return NextResponse.json({ success: true }) + } + ``` + + + + ```tsx {{ filename: 'src/api/example.ts' }} + import type { APIRoute } from 'astro' + import { clerkClient } from '@clerk/astro/server' + + export const GET: APIRoute = async (context) => { + const { userId } = context.locals.auth() + + await clerkClient(context).users.deleteUser(userId) + + return new Response(JSON.stringify({ success: true }), { status: 200 }) + } + ``` + + + + ```ts {{ filename: 'public.ts' }} + import { getAuth, clerkClient } from '@clerk/express' + + app.post('/deleteUser', async (req, res) => { + const { userId } = getAuth(req) + + await clerkClient.users.deleteUser(userId) + + res.status(200).json({ success: true }) + }) + ``` + + + + ```tsx {{ filename: 'app/routes/example.tsx' }} + import { clerkClient, getAuth } from '@clerk/react-router/server' + import type { Route } from './+types/example' + + export async function loader(args: Route.LoaderArgs) { + const { userId } = await getAuth(args) + + await clerkClient.users.deleteUser(userId) + + return { success: true } + } + ``` + + + + ```tsx {{ filename: 'app/routes/api/example.tsx' }} + import { json } from '@tanstack/react-start' + import { createFileRoute } from '@tanstack/react-router' + import { auth, clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + const { userId } = await auth() + + await clerkClient().users.deleteUser(userId) + + return json({ success: true }) + }, + }, + }, + }) + ``` + + diff --git a/docs/_partials/revoke-invitation.mdx b/docs/_partials/revoke-invitation.mdx new file mode 100644 index 0000000000..616581ac54 --- /dev/null +++ b/docs/_partials/revoke-invitation.mdx @@ -0,0 +1,87 @@ + + + ```ts {{ filename: 'app/api/example/route.ts' }} + import { clerkClient } from '@clerk/nextjs/server' + import { NextResponse } from 'next/server' + + export async function GET() { + try { + const client = await clerkClient() + const invitation = await client.invitations.revokeInvitation({ + invitationId: 'invitation_123', + }) + return NextResponse.json({ message: 'Invitation revoked' }) + } catch (error) { + console.log(error) + return NextResponse.json({ error: 'Error revoking invitation' }) + } + } + ``` + + + + ```tsx {{ filename: 'src/api/example.ts' }} + import type { APIRoute } from 'astro' + import { clerkClient } from '@clerk/astro/server' + + export const GET: APIRoute = async (context) => { + await clerkClient(context).invitations.revokeInvitation({ + invitationId: 'invitation_123', + }) + + return new Response(JSON.stringify({ success: true }), { status: 200 }) + } + ``` + + + + ```ts {{ filename: 'public.ts' }} + import { getAuth, clerkClient } from '@clerk/express' + + app.post('/revokeInvitation', async (req, res) => { + await clerkClient.invitations.revokeInvitation({ + invitationId: 'invitation_123', + }) + + res.status(200).json({ success: true }) + }) + ``` + + + + ```tsx {{ filename: 'app/routes/example.tsx' }} + import { clerkClient } from '@clerk/react-router/server' + import type { Route } from './+types/example' + + export async function loader(args: Route.LoaderArgs) { + await clerkClient.invitations.revokeInvitation({ + invitationId: 'invitation_123', + }) + + return { success: true } + } + ``` + + + + ```tsx {{ filename: 'app/routes/api/example.tsx' }} + import { json } from '@tanstack/react-start' + import { createFileRoute } from '@tanstack/react-router' + import { clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + await clerkClient().invitations.revokeInvitation({ + invitationId: 'invitation_123', + }) + + return json({ success: true }) + }, + }, + }, + }) + ``` + + diff --git a/docs/getting-started/quickstart.go.mdx b/docs/getting-started/quickstart.go.mdx index b6b927debb..b846205865 100644 --- a/docs/getting-started/quickstart.go.mdx +++ b/docs/getting-started/quickstart.go.mdx @@ -8,7 +8,7 @@ The [Clerk Go SDK](/docs/reference/go/overview) is built on top of the [Clerk Ba ## Installation -If you're using Go Modules and have a `go.mod` file in your project's root, you can import `clerk-sdk-go` directly: +If you're using Go Modules and have a `go.mod` file in your project's root, you can import `clerk-sdk-go` directly in your `.go` files: ```go import ( @@ -16,7 +16,7 @@ import ( ) ``` -Alternatively, you can `go get` the package explicitly: +Alternatively, you can `go get` the package explicitly and it will add the necessary dependencies to your `go.mod` file: ```sh {{ filename: 'terminal' }} go get -u github.com/clerk/clerk-sdk-go/v2 diff --git a/docs/getting-started/quickstart.js-backend.mdx b/docs/getting-started/quickstart.js-backend.mdx index 8fbebf7735..a1a8ab16bc 100644 --- a/docs/getting-started/quickstart.js-backend.mdx +++ b/docs/getting-started/quickstart.js-backend.mdx @@ -14,23 +14,9 @@ For example, if you wanted to get a list of all users in your application, inste If you are using the JS Backend SDK on its own, you can install it using the following command: - - ```bash {{ filename: 'terminal' }} - npm install @clerk/backend - ``` - - ```bash {{ filename: 'terminal' }} - yarn add @clerk/backend - ``` - - ```bash {{ filename: 'terminal' }} - pnpm add @clerk/backend - ``` - - ```bash {{ filename: 'terminal' }} - bun add @clerk/backend - ``` - + ```npm {{ filename: 'terminal' }} + npm install @clerk/backend + ``` @@ -65,7 +51,9 @@ To access a resource, you must first instantiate a `clerkClient` instance. To use the default `clerkClient` instance, set your `CLERK_SECRET_KEY` [environment variable](/docs/guides/development/clerk-environment-variables#clerk-publishable-and-secret-keys) and then import the `clerkClient` instance from the SDK as shown in the following example: - + ```jsx import { clerkClient } from '@clerk/nextjs/server' @@ -73,7 +61,15 @@ To access a resource, you must first instantiate a `clerkClient` instance. - If you are using Remix, see the following section for how to instantiate `clerkClient`. + ```js + import { clerkClient } from '@clerk/astro/server' + ``` + + + + ```js + import { clerkClient } from '@clerk/express' + ``` @@ -84,19 +80,21 @@ To access a resource, you must first instantiate a `clerkClient` instance. ```js - import { clerkClient } from '@clerk/astro/server' + import { clerkClient } from '@clerk/nuxt/server' ``` - ```js - import { clerkClient } from '@clerk/express' - ``` + If you are using React Router, see the following section for how to instantiate `clerkClient`. + + + + If you are using Remix, see the following section for how to instantiate `clerkClient`. ```js - import { clerkClient } from '@clerk/nuxt/server' + import { clerkClient } from '@clerk/tanstack-react-start/server' ``` @@ -105,7 +103,9 @@ To access a resource, you must first instantiate a `clerkClient` instance. If you would like to customize the behavior of the JS Backend SDK, you can instantiate a `clerkClient` instance yourself by calling `createClerkClient()` and passing in [`options`](#create-clerk-client-options). - + ```jsx import { createClerkClient } from '@clerk/nextjs/server' @@ -118,6 +118,87 @@ To access a resource, you must first instantiate a `clerkClient` instance. ``` + + If you are using Astro, you must pass the [endpoint context](https://docs.astro.build/en/reference/api-reference/#endpoint-context) when invoking the `clerkClient` function. + + ```jsx + import { clerkClient } from '@clerk/astro/server' + + export async function GET(context) { + const { isAuthenticated, userId, redirectToSignIn } = context.locals.auth() + + if (!isAuthenticated) { + return redirectToSignIn() + } + + const user = await clerkClient(context).users.getUser(userId) + + return new Response(JSON.stringify({ user })) + } + ``` + + + + ```js + import { createClerkClient } from '@clerk/express' + + const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) + + const userList = await clerkClient.users.getUserList() + ``` + + + + ```jsx + import { createClerkClient } from '@clerk/fastify' + + const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) + + const userList = await clerkClient.users.getUserList() + ``` + + + + ```tsx {{ filename: 'server/api/users/index.ts' }} + import { createClerkClient } from '@clerk/nuxt/server' + + export default defineEventHandler(async () => { + const config = useRuntimeConfig() + const clerkClient = createClerkClient({ secretKey: config.clerk.secretKey }) + const userList = await clerkClient.users.getUserList() + + return { userList } + }) + ``` + + + + ```tsx {{ filename: 'app/routes/example.tsx' }} + import { createClerkClient } from '@clerk/react-router/api.server' + + export async function loader(args: Route.LoaderArgs) { + const userList = await createClerkClient({ + secretKey: process.env.CLERK_SECRET_KEY, + }).users.getUserList() + + return { + userList: JSON.stringify(userList), + } + } + + export default function Users({ loaderData }: Route.ComponentProps) { + return ( +
+

List of users

+
+                {JSON.stringify(loaderData, null, 2)}
+              
+
+ ) + } + ``` +
+ If you are using Remix, you must instantiate `clerkClient` by calling the `createClerkClient()` function and passing in [`options`](#create-clerk-client-options). @@ -183,90 +264,23 @@ To access a resource, you must first instantiate a `clerkClient` instance.
- - ```jsx - import { createClerkClient } from '@clerk/fastify' - - const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) - - const userList = await clerkClient.users.getUserList() - ``` - - - - If you are using Astro, you must pass the [endpoint context](https://docs.astro.build/en/reference/api-reference/#endpoint-context) when invoking the `clerkClient` function. - - ```jsx - import { clerkClient } from '@clerk/astro/server' - - export async function GET(context) { - const { isAuthenticated, userId, redirectToSignIn } = context.locals.auth() - - if (!isAuthenticated) { - return redirectToSignIn() - } - - const user = await clerkClient(context).users.getUser(userId) - - return new Response(JSON.stringify({ user })) - } - ``` - - ```tsx {{ filename: 'app/routes/api/example.tsx' }} - import { createClerkClient } from '@clerk/backend' + import { clerkClient } from '@clerk/tanstack-react-start/server' import { json } from '@tanstack/react-start' import { createServerFileRoute } from '@tanstack/react-start/server' export const ServerRoute = createServerFileRoute().methods({ GET: async ({ request, params }) => { - const clerkClient = createClerkClient({ secretKey: import.meta.env.CLERK_SECRET_KEY }) - - const userList = await clerkClient.users.getUserList() + const userList = await clerkClient({ + secretKey: import.meta.env.CLERK_SECRET_KEY, + }).users.getUserList() return json({ userList }) }, }) ``` - - - - ```js - import { createClerkClient } from '@clerk/express' - - const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) - - const userList = await clerkClient.users.getUserList() - ``` - - ```js - const Clerk = require('@clerk/express') - - const clerkClient = Clerk.createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) - - clerkClient.sessions - .getSessionList() - .then((sessions) => console.log(sessions)) - .catch((error) => console.error(error)) - ``` - - - - - ```tsx {{ filename: 'server/api/users/index.ts' }} - import { createClerkClient } from '@clerk/nuxt/server' - - export default defineEventHandler(async () => { - const config = useRuntimeConfig() - const clerkClient = createClerkClient({ secretKey: config.clerk.secretKey }) - const userList = await clerkClient.users.getUserList() - - return { userList } - }) - ``` - @@ -327,9 +341,11 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan The following examples demonstrate how to retrieve the authenticated user's ID using framework-specific auth helpers and how to use the JS Backend SDK's [`getUser()`](/docs/reference/backend/user/get-user) method to get the [Backend `User` object](/docs/reference/backend/types/backend-user). + **If your SDK isn't listed, you can use the comments in the example to help you adapt it to your SDK.** + {/* TODO: The following Tabs example is duplicated in the guides/how-clerk-works/session-tokens.mdx file. It cannot be a partial to be reused across both files because this example includes a partial and partials cannot include partials. Also, the text in each of these tabs is removed in the other file as its not relevant to that file's example. So keep these two Tabs examples in sync please. */} - + For Next.js, the `Auth` object is accessed using the `auth()` helper in App Router apps and the `getAuth()` function in Pages Router apps. [Learn more about using these helpers](/docs/nextjs/guides/users/reading#server-side). @@ -340,7 +356,9 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan import { auth, clerkClient } from '@clerk/nextjs/server' export async function GET() { - // Use `auth()` to access `isAuthenticated` and the user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = await auth() // Protect the route by checking if the user is signed in @@ -350,7 +368,9 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan const client = await clerkClient() - // Use the JS Backend SDK's `getUser()` method to get the Backend User object + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart const user = await client.users.getUser(userId) // Return the Backend User object @@ -367,9 +387,12 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan ```tsx {{ filename: 'src/api/example.ts' }} import { clerkClient } from '@clerk/astro/server' + import type { APIRoute } from 'astro' - export async function GET(context) { - // Use `locals.auth()` to access `isAuthenticated` and the user's ID + export const GET: APIRoute = async (context) => { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = context.locals.auth() // Protect the route by checking if the user is signed in @@ -377,7 +400,9 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan return new Response('Unauthorized', { status: 401 }) } - // Use the JS Backend SDK's `getUser()` method to get the Backend User object + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart const user = await clerkClient(context).users.getUser(userId) // Return the Backend User object @@ -397,7 +422,9 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) app.get('/user', async (req, res) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = getAuth(req) // Protect the route by checking if the user is signed in @@ -405,7 +432,10 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan res.status(401).json({ error: 'User not authenticated' }) } - // Use the JS Backend SDK's `getUser()` method to get the Backend User object + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the `getUser()` method to get the Backend User object const user = await clerkClient.users.getUser(userId) // Return the Backend User object @@ -423,7 +453,9 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan import type { Route } from './+types/profile' export async function loader(args: Route.LoaderArgs) { - // Use `getAuth()` to access `isAuthenticated` and the user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = await getAuth(args) // Protect the route by checking if the user is signed in @@ -443,85 +475,37 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan - For Remix, the `Auth` object is accessed using the `getAuth()` function. [Learn more about using `getAuth()`](/docs/remix/guides/users/reading#get-auth). Use the following tabs to see examples of how to use the `getAuth()` helper to get the user's ID in Remix Loader and Action functions. - - - ```tsx {{ filename: 'routes/profile.tsx' }} - import { LoaderFunction, redirect } from '@remix-run/node' - import { getAuth } from '@clerk/remix/ssr.server' - import { createClerkClient } from '@clerk/remix/api.server' - - export const loader: LoaderFunction = async (args) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID - const { isAuthenticated, userId } = await getAuth(args) - - // If there is no userId, then redirect to sign-in route - if (!isAuthenticated) { - return redirect('/sign-in?redirect_url=' + args.request.url) - } - - // Use the JS Backend SDK's `getUser()` method to get the Backend User object - const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser( - userId, - ) - - // Return the Backend User object - return { serialisedUser: JSON.stringify(user) } - } - ``` - - ```tsx {{ filename: 'routes/profile.tsx' }} - import { ActionFunction, redirect } from '@remix-run/node' - import { getAuth } from '@clerk/remix/ssr.server' - import { createClerkClient } from '@clerk/remix/api.server' - - export const action: ActionFunction = async (args) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID - const { isAuthenticated, userId } = await getAuth(args) - - // If there is no userId, then redirect to sign-in route - if (!isAuthenticated) { - return redirect('/sign-in?redirect_url=' + args.request.url) - } - - const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser( - userId, - ) - - // Return the user - return { serialisedUser: JSON.stringify(user) } - } - ``` - - - - - For Tanstack React Start, the `Auth` object is accessed using the `getAuth()` function. [Learn more about using `getAuth()`](/docs/tanstack-react-start/guides/users/reading#server-side). + For Tanstack React Start, the `Auth` object is accessed using the `auth()` function. [Learn more about using `auth()`](/docs/tanstack-react-start/guides/users/reading#server-side). ```tsx {{ filename: 'app/routes/api/example.tsx' }} - import { createClerkClient } from '@clerk/backend' import { json } from '@tanstack/react-start' - import { createAPIFileRoute } from '@tanstack/react-start/api' - import { getAuth } from '@clerk/tanstack-react-start/server' - - export const Route = createAPIFileRoute('/api/example')({ - GET: async ({ request, params }) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID - const { isAuthenticated, userId } = await getAuth(req) - - // Protect the route by checking if the user is signed in - if (!isAuthenticated) { - return json({ error: 'Unauthorized' }, { status: 401 }) - } - - // Instantiate the JS Backend SDK - const clerkClient = createClerkClient({ secretKey: import.meta.env.CLERK_SECRET_KEY }) - - // Use the JS Backend SDK's `getUser()` method to get the Backend User object - const user = await clerkClient.users.getUser(userId) - - // Return the Backend User object - return json({ user }) + import { createFileRoute } from '@tanstack/react-router' + import { auth, clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = await auth() + + // Protect the route by checking if the user is signed in + if (!isAuthenticated) { + return json({ error: 'Unauthorized' }, { status: 401 }) + } + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the `getUser()` method to get the Backend User object + const user = await clerkClient().users.getUser(userId) + + // Return the Backend User object + return json({ user }) + }, + }, }, }) ``` diff --git a/docs/guides/configure/auth-strategies/social-connections/overview.mdx b/docs/guides/configure/auth-strategies/social-connections/overview.mdx index 4568710bf7..06dcc225ad 100644 --- a/docs/guides/configure/auth-strategies/social-connections/overview.mdx +++ b/docs/guides/configure/auth-strategies/social-connections/overview.mdx @@ -74,7 +74,7 @@ Use the following tabs to see how to add additional OAuth scopes to the ` + + ```tsx {{ filename: 'app/api/notion/route.tsx' }} + import { auth, clerkClient } from '@clerk/nextjs/server' + import { NextResponse } from 'next/server' + + export async function GET() { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = await auth() + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return NextResponse.json({ message: 'User not found' }) + } + + const provider = 'notion' + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + const client = await clerkClient() + + // Use the JS Backend SDK to get the user's OAuth access token + const clerkResponse = await client.users.getUserOauthAccessToken(userId, provider) + const accessToken = clerkResponse.data[0].token || '' + if (!accessToken) { + return NextResponse.json({ message: 'Access token not found' }, { status: 401 }) + } + + // Fetch the user data from the Notion API + // This endpoint fetches a list of users + // https://developers.notion.com/reference/get-users + const notionUrl = 'https://api.notion.com/v1/users' + + const notionResponse = await fetch(notionUrl, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Notion-Version': '2022-06-28', + }, + }) + + // Handle the response from the Notion API + const notionData = await notionResponse.json() + + return NextResponse.json({ message: notionData }) + } + ``` + + + + ```tsx {{ filename: 'src/api/notion.ts' }} + import { clerkClient } from '@clerk/astro/server' + import type { APIRoute } from 'astro' + + export const GET: APIRoute = async (context) => { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/references/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = context.locals.auth() + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return new Response('Unauthorized', { status: 401 }) + } + + const provider = 'notion' + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the JS Backend SDK to get the user's OAuth access token + const clerkResponse = await clerkClient(context).users.getUserOauthAccessToken(userId, provider) + const accessToken = clerkResponse.data[0].token || '' + if (!accessToken) { + return new Response('Access token not found', { status: 401 }) + } + + // Fetch the user data from the Notion API + // This endpoint fetches a list of users + // https://developers.notion.com/reference/get-users + const notionUrl = 'https://api.notion.com/v1/users' + + const notionResponse = await fetch(notionUrl, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Notion-Version': '2022-06-28', + }, + }) + + // Handle the response from the Notion API + const notionData = await notionResponse.json() + + // Return the Notion data + return new Response(JSON.stringify({ notionData })) + } + ``` + + + + ```js {{ filename: 'notion.js' }} + import { createClerkClient, getAuth } from '@clerk/express' + import express from 'express' + + const app = express() + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) + + app.get('/user', async (req, res) => { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = getAuth(req) + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + res.status(401).json({ error: 'User not authenticated' }) + } + + const provider = 'notion' + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the JS Backend SDK to get the user's OAuth access token + const clerkResponse = await clerkClient.users.getUserOauthAccessToken(userId, provider) + const accessToken = clerkResponse.data[0].token || '' + if (!accessToken) { + res.status(401).json({ error: 'Access token not found' }) + } + + // Fetch the user data from the Notion API + // This endpoint fetches a list of users + // https://developers.notion.com/reference/get-users + const notionUrl = 'https://api.notion.com/v1/users' + + const notionResponse = await fetch(notionUrl, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Notion-Version': '2022-06-28', + }, + }) + + // Handle the response from the Notion API + const notionData = await notionResponse.json() + + // Return the Notion data + res.json(notionData) + }) + ``` + + + + ```js + import { createClerkClient } from '@clerk/backend' + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) + + async function getNotionData(request) { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = request.auth + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return null + } + + // Use the JS Backend SDK to get the user's OAuth access token + const provider = 'notion' + const clerkResponse = await clerkClient.users.getUserOauthAccessToken(userId, provider) + const accessToken = clerkResponse.data[0].token || '' + if (!accessToken) { + return null + } + + // Fetch the user data from the Notion API + // This endpoint fetches a list of users + // https://developers.notion.com/reference/get-users + const notionUrl = 'https://api.notion.com/v1/users' + + const notionResponse = await fetch(notionUrl, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Notion-Version': '2022-06-28', + }, + }) + + // Handle the response from the Notion API + const notionData = await notionResponse.json() + + // Return the Notion data + return notionData + } + ``` + + + + ```tsx {{ filename: 'app/routes/notion.tsx' }} + import { redirect } from 'react-router' + import { getAuth } from '@clerk/react-router/ssr.server' + import { createClerkClient } from '@clerk/react-router/api.server' + import type { Route } from './+types/notion' + + export async function loader(args: Route.LoaderArgs) { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = await getAuth(args) + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return redirect('/sign-in?redirect_url=' + args.request.url) + } + + const provider = 'notion' + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the JS Backend SDK to get the user's OAuth access token + const clerkResponse = await createClerkClient({ + secretKey: process.env.CLERK_SECRET_KEY, + }).users.getUserOauthAccessToken(userId, provider) + const accessToken = clerkResponse.data[0].token || '' + if (!accessToken) { + return redirect('/sign-in?redirect_url=' + args.request.url) + } + + // Fetch the user data from the Notion API + // This endpoint fetches a list of users + // https://developers.notion.com/reference/get-users + const notionUrl = 'https://api.notion.com/v1/users' + + const notionResponse = await fetch(notionUrl, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Notion-Version': '2022-06-28', + }, + }) + + // Handle the response from the Notion API + const notionData = await notionResponse.json() + + // Return the Notion data + return { + notionData: JSON.stringify(notionData), + } + } + ``` + + + + ```tsx {{ filename: 'app/routes/api/notion.tsx' }} + import { json } from '@tanstack/react-start' + import { createFileRoute } from '@tanstack/react-router' + import { auth, clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/notion')({ + server: { + handlers: { + GET: async () => { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = await auth() + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return new Response('User not authenticated', { + status: 404, + }) + } + + const provider = 'notion' + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the JS Backend SDK to get the user's OAuth access token + const clerkResponse = await clerkClient().users.getUserOauthAccessToken(userId, provider) + const accessToken = clerkResponse.data[0].token || '' + if (!accessToken) { + return new Response('Access token not found', { + status: 401, + }) + } + + // Fetch the user data from the Notion API + // This endpoint fetches a list of users + // https://developers.notion.com/reference/get-users + const notionUrl = 'https://api.notion.com/v1/users' + + const notionResponse = await fetch(notionUrl, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Notion-Version': '2022-06-28', + }, + }) + + // Handle the response from the Notion API + const notionData = await notionResponse.json() + + return json(notionData) + }, + }, + }, + }) + ``` + + ## Add a social connection after sign-up diff --git a/docs/guides/configure/session-tasks.mdx b/docs/guides/configure/session-tasks.mdx index 935bc800e0..e7d50065bb 100644 --- a/docs/guides/configure/session-tasks.mdx +++ b/docs/guides/configure/session-tasks.mdx @@ -25,9 +25,7 @@ After authentication, users enter one of three states: When authenticating, sessions remain `pending` until users complete the required tasks. By default, `pending` sessions are treated as signed-out across Clerk's authentication context. -## Implementation - -### Displaying tasks +## Displaying tasks Once enabled in the Clerk Dashboard, tasks are **embedded by default** within the `` and `` components. For more customization, you can opt out of using the `` and `` components and [create custom pages for tasks](/docs/guides/development/custom-flows/overview#session-tasks). @@ -40,12 +38,10 @@ The following table lists the available tasks and their corresponding components > [!IMPORTANT] > Personal accounts being disabled by default was released on 08-22-2025. Applications created before this date will not be able to see the **Allow personal accounts** setting, because personal accounts were enabled by default. -If the prebuilt components don't meet your specific needs or if you require more control over the logic, you can also build your own UI using the `Session.currentTask` property to check if the user has pending session tasks. Refer to the [custom flows documentation](/docs/guides/development/custom-flows/overview) for specific implementation guides. +If the prebuilt components don't meet your specific needs or if you require more control over the logic, you can also build your own UI using the `Session.currentTask` property to check if the user has pending session tasks. To access the `Session.currentTask` property, you can use either the `useSession()` hook for React-based applications or `window.Clerk` for other frameworks. - + ```jsx - import { useSession } from '@clerk/clerk-react' - const { session } = useSession() if (session?.currentTask) { @@ -64,130 +60,100 @@ If the prebuilt components don't meet your specific needs or if you require more ``` -### Redirecting to tasks +## Redirecting to tasks When users have pending session tasks, you can redirect them to specific pages or components to complete these requirements. This is useful when you want to handle session tasks outside of the default `` and `` components. -#### Using the `taskUrls` option +### Using the `taskUrls` option The `taskUrls` option allows you to specify custom URL paths where users are redirected after sign-up or sign-in when specific session tasks need to be completed. This allows you to still use `` and `` but have tasks with custom pages. - - - Configure the `taskUrls` option in your root layout to specify where users should be redirected for different session tasks. +The `taskUrls` option is available wherever you initialize the Clerk integration. For most SDKs, it's ``. - ```tsx {{ filename: 'app/layout.tsx' }} - import { ClerkProvider } from '@clerk/nextjs' - - export default function RootLayout({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ) - } - ``` - - - - Create a custom page that imports the `TaskChooseOrganization` component to handle the task. - - ```tsx {{ filename: 'app/onboarding/choose-organization/page.tsx' }} - import { TaskChooseOrganization } from '@clerk/nextjs' - - export default function ChooseOrganizationPage() { - return - } - ``` - - - -#### Using the `RedirectToTasks` control component - -The `` control component redirects users to the appropriate task page when they have pending session tasks. +```tsx + + {children} + +``` -```tsx {{ filename: 'app/dashboard/layout.tsx' }} -import { SignedOut, RedirectToTasks } from '@clerk/nextjs' +Then, create a page at that URL path that imports the [``](/docs/reference/components/authentication/task-choose-organization) component to handle the task. -export default function Layout({ children }: { children: React.ReactNode }) { - return ( - <> - - - - {children} - - ) +```tsx {{ filename: 'app/onboarding/choose-organization/page.tsx' }} +export default function Page() { + return } ``` -{/* TODO: Add examples for other sdk's that support middleware */} +### Using the `` control component -#### Middleware-based redirects - -For `auth.protect()`, signed-out users will be redirected to the sign-in page. In the following example, `pending` users will be redirected to the sign-in page, where the `` component will prompt them to fulfill the session tasks, which in this case is selecting or creating an organization. Once finished, their session will move from `pending` to an `active` (signed-in) state. - -```tsx {{ filename: 'middleware.ts', mark: [[6, 8]] }} -import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' - -const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/forum(.*)']) - -export default clerkMiddleware(async (auth, req) => { - // pending users won't be able to access protected routes - // and will be redirected to the sign-in page - if (isProtectedRoute(req)) await auth.protect() -}) +The `` control component redirects users to the appropriate task page when they have pending session tasks. -export const config = { - matcher: [ - // Skip Next.js internals and all static files, unless found in search params - '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', - // Always run for API routes - '/(api|trpc)(.*)', - ], -} +```tsx +<> + + + + ``` -For `auth().isAuthenticated`, it would return `false` if the user has a `pending` session. Pending users will be redirected to the sign-in page, where the `` component will prompt them to fulfill the session tasks. Once finished, their session will move from `pending` to a `signed-in` state. +### Middleware-based redirects -```tsx {{ filename: 'app/middleware.ts', mark: [[6, 12]] }} -import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' +You can use the `isAuthenticated` property in middleware to protect routes. By default, it will return `false` if the user has a `pending` session. Then, you can handle how to respond to pending users, such as redirecting them to the sign-in page using the `redirectToSignIn()` method. +```tsx const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/forum(.*)']) export default clerkMiddleware(async (auth, req) => { const { isAuthenticated, redirectToSignIn } = await auth() // `isAuthenticated` will be `false` for pending users - // and they will be redirected to the sign-in page + // and pending users won't be able to access protected routes if (!isAuthenticated && isProtectedRoute(req)) { + // Add logic to handle pending users + // This example redirects pending users to the sign-in page + // so they can fulfill the session tasks return redirectToSignIn() } }) - -export const config = { - matcher: [ - // Skip Next.js internals and all static files, unless found in search params - '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', - // Always run for API routes - '/(api|trpc)(.*)', - ], -} ``` -### Session handling + + For `auth.protect()`, signed-out users will be redirected to the sign-in page. In the following example, `pending` users will be redirected to the sign-in page, where the `` component will prompt them to fulfill the session tasks. Once finished, their session will move from `pending` to an `active` (signed-in) state. + + ```tsx {{ filename: 'middleware.ts', mark: [[6, 8]] }} + import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' + + const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/forum(.*)']) + + export default clerkMiddleware(async (auth, req) => { + // pending users won't be able to access protected routes + // and will be redirected to the sign-in page + if (isProtectedRoute(req)) await auth.protect() + }) + + export const config = { + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', + // Always run for API routes + '/(api|trpc)(.*)', + ], + } + ``` + + +## Session handling By default, `pending` sessions are treated as signed-out across Clerk's authentication context. Some control components and authentication utilities accept a `treatPendingAsSignedOut` prop to control how `pending` sessions are handled: - `true` (default): Treats `pending` sessions as signed-out. Users can't access protected content or routes. - `false`: Treats `pending` sessions as signed-in. Users can access protected content or routes. -#### Control components +### Control components ', '', '']}> @@ -278,11 +244,11 @@ By default, `pending` sessions are treated as signed-out across Clerk's authenti -#### Authentication utilities +### Authentication utilities -The `useAuth()` hook and helpers that access the [`auth` object](/docs/reference/backend/types/auth-object), such as `getAuth()` or `request.auth`, will return `null` if the user has a `pending` session. Most utilities accept a `treatPendingAsSignedOut` option that defaults to `true`. You can pass `false` to treat `pending` sessions as signed-in. +The `useAuth()` hook and helpers that access the [`Auth` object](/docs/reference/backend/types/auth-object), such as `getAuth()` or `request.auth`, will return `null` if the user has a `pending` session. Most utilities accept a `treatPendingAsSignedOut` option that defaults to `true`. You can pass `false` to treat `pending` sessions as signed-in. -##### Example: Personal accounts disabled +#### Example: Personal accounts disabled When organizations are enabled, [personal accounts are disabled by default](/docs/guides/organizations/overview#allow-personal-accounts) and your users will be required to select or create an organization after authenticating. Until completed, their session remains `pending`. Pages that are protected using Clerk's protection utilities will treat the user's session as signed-out. @@ -309,57 +275,62 @@ export default function Dashboard() { } ``` - - For `auth()`, `isAuthenticated` would return `false` and `userId` and `orgId` would return `null` if the user has a `pending` session. - - ```tsx {{ filename: 'app/page.tsx' }} - import { auth } from '@clerk/nextjs/server' +For helpers that access the [`Auth` object](/docs/reference/backend/types/auth-object), `isAuthenticated` would return `false` and `userId` and `orgId` would return `null` if the user has a `pending` session. This example uses the Next.js-specific [`auth()`](/docs/reference/nextjs/app-router/auth) helper, but you can use the comments in the example to help you adapt it to your SDK. - export default async function Page() { - const { isAuthenticated, userId, orgId } = await auth() +```tsx {{ filename: 'app/page.tsx' }} +import { auth } from '@clerk/nextjs/server' - if (!isAuthenticated) { - return ( -

- User has no session, or has a pending session. They either need to sign in, or they need to - complete pending session tasks by selecting or creating an organization. -

- ) - } +export default async function Page() { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId, orgId } = await auth() + if (!isAuthenticated) { return (

- User {userId} has a valid session and {orgId} is defined + User has no session, or has a pending session. They either need to sign in, or they need to + complete pending session tasks by selecting or creating an organization.

) } - ``` - By default, users with a `pending` session are treated as signed-out, and their `userId` will not be available. However, in some cases, you may want to access the user's ID even if their session is still `pending`. In these cases, you can set `treatPendingAsSignedOut` to `false`, which will treat `pending` sessions as signed-in and allow you to access the `userId`. + return ( +

+ User {userId} has a valid session and {orgId} is defined +

+ ) +} +``` - ```tsx {{ filename: 'app/api/get-teams/route.tsx' }} - import { auth } from '@clerk/nextjs/server' +#### Example: Accessing the `userId` for pending sessions - export const POST = async () => { - // `treatPendingAsSignedOut` is set to `false` to allow access to the `userId` for pending sessions - const { isAuthenticated, userId, has } = await auth({ treatPendingAsSignedOut: false }) +By default, users with a `pending` session are treated as signed-out, and their `userId` will not be available. However, in some cases, you may want to access the user's ID even if their session is still `pending`. In these cases, you can set `treatPendingAsSignedOut` to `false`, which will treat `pending` sessions as signed-in and allow you to access the `userId`. This example uses the Next.js-specific [`auth()`](/docs/reference/nextjs/app-router/auth) helper, but you can use the comments in the example to help you adapt it to your SDK. - // Check if the user is signed-out - if (!isAuthenticated) { - return Response.json({ error: 'User is signed-out' }, { status: 401 }) - } +```tsx {{ filename: 'app/api/get-teams/route.tsx' }} +import { auth } from '@clerk/nextjs/server' - // Now the pending user's `userId` can be used for your use case - // This is a basic example of creating a resource using the `userId` - try { - const newResource = await resources.create({ - userId, - }) +export const POST = async () => { + // `treatPendingAsSignedOut` is set to `false` to allow access to the `userId` for pending sessions + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId, has } = await auth({ treatPendingAsSignedOut: false }) - return Response.json({ data: newResource }, { status: 201 }) - } catch (error) { - return Response.json({ error: 'Failed to create resource' }, { status: 500 }) - } + // Check if the user is signed-out + if (!isAuthenticated) { + return Response.json({ error: 'User is signed-out' }, { status: 401 }) } - ``` -
+ + // Now the pending user's `userId` can be used for your use case + // This is a basic example of creating a resource using the `userId` + try { + const newResource = await resources.create({ + userId, + }) + + return Response.json({ data: newResource }, { status: 201 }) + } catch (error) { + return Response.json({ error: 'Failed to create resource' }, { status: 500 }) + } +} +``` diff --git a/docs/guides/development/custom-flows/account-updates/user-impersonation.mdx b/docs/guides/development/custom-flows/account-updates/user-impersonation.mdx index 9529495049..1ed45681c2 100644 --- a/docs/guides/development/custom-flows/account-updates/user-impersonation.mdx +++ b/docs/guides/development/custom-flows/account-updates/user-impersonation.mdx @@ -230,7 +230,7 @@ This guide will walk you through how to build a custom flow that handles user im To sign in as a different user, you must supply an actor token when creating a session. - Create the `generateActorToken+api.tsx` file with the following code. This creates an API route that will call Clerk's Backend API [`/actor_tokens`](/docs/reference/backend-api/tag/actor-tokens/post/actor_tokens) endpoint to create an actor token. + Create the `generateActorToken+api.tsx` file with the following code. This creates an API route that will call Clerk's Backend API [`/actor_tokens`](/docs/reference/backend-api/tag/actor-tokens/post/actor_tokens){{ target: '_blank' }} endpoint to create an actor token. ```tsx {{ filename: 'app/generateActorToken+api.tsx', collapsible: true }} export async function POST(req: Request) { diff --git a/docs/guides/development/custom-flows/authentication/application-invitations.mdx b/docs/guides/development/custom-flows/authentication/application-invitations.mdx index 130aa4aa78..133d504dbf 100644 --- a/docs/guides/development/custom-flows/authentication/application-invitations.mdx +++ b/docs/guides/development/custom-flows/authentication/application-invitations.mdx @@ -9,7 +9,7 @@ When a user visits an [invitation](/docs/guides/users/inviting) link, Clerk firs **If no redirect URL is specified**, the user will be redirected to the appropriate Account Portal page (either [sign-up](/docs/guides/customizing-clerk/account-portal#sign-up) or [sign-in](/docs/guides/customizing-clerk/account-portal#sign-in)), or to the custom sign-up/sign-in pages that you've configured for your application. -**If you specified [a redirect URL when creating the invitation](/docs/guides/users/inviting#redirect-url)**, you must handle the authentication flows in your code for that page. You can either embed the [``](/docs/reference/components/authentication/sign-in) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can rebuild the existing Clerk flows using the Clerk API. +**If you specified [a redirect URL when creating the invitation](/docs/guides/users/inviting#with-a-redirect-url)**, you must handle the authentication flows in your code for that page. You can either embed the [``](/docs/reference/components/authentication/sign-in) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can rebuild the existing Clerk flows using the Clerk API. This guide demonstrates how to use Clerk's API to build a custom flow for accepting application invitations. diff --git a/docs/guides/development/custom-flows/billing/checkout-existing-payment-method.mdx b/docs/guides/development/custom-flows/billing/checkout-existing-payment-method.mdx index 55ff076668..e15109c32a 100644 --- a/docs/guides/development/custom-flows/billing/checkout-existing-payment-method.mdx +++ b/docs/guides/development/custom-flows/billing/checkout-existing-payment-method.mdx @@ -32,7 +32,7 @@ This guide will walk you through how to build a custom user interface for a chec 1. Uses the [`usePaymentMethods()`](/docs/reference/hooks/use-payment-methods) hook to fetch the user's existing payment methods. 1. Assumes that you have already have a valid `planId`, which you can acquire in many ways: - [Copy from the Clerk Dashboard](https://dashboard.clerk.com/~/billing/plans?tab=user). - - Use the [Clerk Backend API](/docs/reference/backend-api/tag/commerce/get/commerce/plans#tag/commerce/get/commerce/plans). + - Use the [Clerk Backend API](/docs/reference/backend-api/tag/commerce/get/commerce/plans#tag/commerce/get/commerce/plans){{ target: '_blank' }}. - Use the new [`usePlans()`](/docs/reference/hooks/use-plans) hook to get the plan details. This example is written for Next.js App Router but can be adapted for any React-based framework. diff --git a/docs/guides/development/custom-flows/billing/checkout-new-payment-method.mdx b/docs/guides/development/custom-flows/billing/checkout-new-payment-method.mdx index 8a5dc44e76..8a4162ce31 100644 --- a/docs/guides/development/custom-flows/billing/checkout-new-payment-method.mdx +++ b/docs/guides/development/custom-flows/billing/checkout-new-payment-method.mdx @@ -36,7 +36,7 @@ For the custom flow that allows users to add a new payment method to their accou 1. Uses the [`usePaymentElement()`](/docs/reference/hooks/use-payment-element) hook to control the payment element, which is rendered by ``. 1. Assumes that you have already have a valid `planId`, which you can acquire in many ways. - [Copy from the Clerk Dashboard](https://dashboard.clerk.com/~/billing/plans?tab=user). - - Use the [Clerk Backend API](/docs/reference/backend-api/tag/commerce/get/commerce/plans#tag/commerce/get/commerce/plans). + - Use the [Clerk Backend API](/docs/reference/backend-api/tag/commerce/get/commerce/plans#tag/commerce/get/commerce/plans){{ target: '_blank' }}. - Use the new [`usePlans()`](/docs/reference/hooks/use-plans) hook to get the plan details. This example is written for Next.js App Router but can be adapted for any React-based framework. diff --git a/docs/guides/organizations/sso.mdx b/docs/guides/organizations/sso.mdx index ef132a1c46..4b68f8718e 100644 --- a/docs/guides/organizations/sso.mdx +++ b/docs/guides/organizations/sso.mdx @@ -50,10 +50,10 @@ Enterprise SSO connections are enforced on a per-domain basis in organizations, ### Remove a member from your organization -When a user is tied to an organization through their enterprise connection, they cannot leave the organization themselves, but they can be removed either in the Clerk Dashboard, using [Clerk's Backend API](/docs/reference/backend-api/tag/organization-memberships/delete/organizations/\{organization_id}/memberships/\{user_id}) endpoint, or by another organization member with the [manage members permission](/docs/guides/organizations/roles-and-permissions#system-permissions) (`org:sys_memberships:manage`). However, the user will be added back to the organization on next sign-in, unless they are removed from the IdP or the enterprise connection is no longer associated with the organization. +When a user is tied to an organization through their enterprise connection, they cannot leave the organization themselves, but they can be removed either in the Clerk Dashboard, using [Clerk's Backend API](/docs/reference/backend-api/tag/organization-memberships/delete/organizations/\{organization_id}/memberships/\{user_id}){{ target: '_blank' }} endpoint, or by another organization member with the [manage members permission](/docs/guides/organizations/roles-and-permissions#system-permissions) (`org:sys_memberships:manage`). However, the user will be added back to the organization on next sign-in, unless they are removed from the IdP or the enterprise connection is no longer associated with the organization. ## Update an organization from an existing enterprise connection When transitioning an enterprise connection to a new organization, existing members will remain part of the original organization. However, they will automatically join the new organization upon their next sign-in. -To remove members from the original organization, you have two options: utilize [Clerk's Backend API](/docs/reference/backend-api/tag/organization-memberships/delete/organizations/\{organization_id}/memberships/\{user_id}) or manage memberships directly through the Clerk Dashboard. +To remove members from the original organization, you have two options: utilize [Clerk's Backend API](/docs/reference/backend-api/tag/organization-memberships/delete/organizations/\{organization_id}/memberships/\{user_id}){{ target: '_blank' }} or manage memberships directly through the Clerk Dashboard. diff --git a/docs/guides/sessions/customize-session-tokens.mdx b/docs/guides/sessions/customize-session-tokens.mdx index 9d010feae2..32e858fad5 100644 --- a/docs/guides/sessions/customize-session-tokens.mdx +++ b/docs/guides/sessions/customize-session-tokens.mdx @@ -23,11 +23,11 @@ This guide will show you how to customize a session token to include additional ## Use the custom claims in your application - The [`Auth`](/docs/reference/backend/types/auth-object) object includes a `sessionClaims` property that contains the custom claims you added to your session token. Accessing the `Auth` object differs depending on the framework you are using. See the [reference doc](/docs/reference/backend/types/auth-object) for more information. + The [`Auth`](/docs/reference/backend/types/auth-object) object includes a `sessionClaims` property that contains the custom claims you added to your session token. **Accessing the `Auth` object differs depending on the SDK you're using. See the [reference doc](/docs/reference/backend/types/auth-object) for more information.** The following example demonstrates how to access the `fullName` and `primaryEmail` claims that were added to the session token in the last step. - + For Next.js, the `Auth` object is accessed using the `auth()` helper in App Router apps and the `getAuth()` function in Pages Router apps. [Learn more about using these helpers](/docs/nextjs/guides/users/reading#server-side). @@ -37,7 +37,12 @@ This guide will show you how to customize a session token to include additional import { NextResponse } from 'next/server' export async function GET() { - const { sessionClaims } = await auth() + // Use `auth()` to access the user's session claims + const { isAuthenticated, sessionClaims } = await auth() + + if (!isAuthenticated) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } const fullName = sessionClaims.fullName @@ -52,8 +57,12 @@ This guide will show you how to customize a session token to include additional import type { NextApiRequest, NextApiResponse } from 'next' export default async function handler(req: NextApiRequest, res: NextApiResponse) { - // Use `getAuth()` to access `isAuthenticated` and the user's ID and session claims - const { sessionClaims } = getAuth(req) + // Use `getAuth()` to access the user's session claims + const { isAuthenticated, sessionClaims } = getAuth(req) + + if (!isAuthenticated) { + return res.status(401).json({ error: 'Unauthorized' }) + } const fullName = sessionClaims.fullName @@ -72,10 +81,9 @@ This guide will show you how to customize a session token to include additional import type { APIRoute } from 'astro' export const GET: APIRoute = async ({ locals }) => { - // Use `locals.auth()` to access `isAuthenticated` and the user's ID and session claims - const { isAuthenticated, userId, sessionClaims } = await locals.auth() + // Use `locals.auth()` to access the user's session claims + const { isAuthenticated, sessionClaims } = await locals.auth() - // Protect the route by checking if the user is signed in if (!isAuthenticated) { return new Response('Unauthorized', { status: 401 }) } @@ -102,9 +110,13 @@ This guide will show you how to customize a session token to include additional // Apply `clerkMiddleware()` to all routes app.use(clerkMiddleware()) - // Use `getAuth()` to get the session claims const getSessionClaims = (req, res, next) => { - const { sessionClaims } = getAuth(req) + // Use `getAuth()` to access the user's session claims + const { isAuthenticated, sessionClaims } = getAuth(req) + + if (!isAuthenticated) { + return res.status(401).json({ error: 'Unauthorized' }) + } const fullName = sessionClaims.fullName @@ -115,7 +127,6 @@ This guide will show you how to customize a session token to include additional app.get('/profile', requireAuth(), getSessionClaims) - // Start the server and listen on the specified port app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`) }) @@ -197,7 +208,6 @@ This guide will show you how to customize a session token to include additional // Use `getAuth()` to access `isAuthenticated` and the user's ID and session claims const { isAuthenticated, sessionClaims } = await getAuth(args) - // Protect the route by checking if the user is signed in if (!isAuthenticated) { return redirect('/sign-in?redirect_url=' + args.request.url) } @@ -223,55 +233,32 @@ This guide will show you how to customize a session token to include additional ``` - - For Remix, the `Auth` object is accessed using the `getAuth()` function. [Learn more about using `getAuth()`](/docs/remix/guides/users/reading#get-auth). - - ```tsx {{ filename: 'routes/profile.tsx' }} - import { LoaderFunction, redirect } from '@remix-run/node' - import { getAuth } from '@clerk/remix/ssr.server' - import { createClerkClient } from '@clerk/remix/api.server' - - export const loader: LoaderFunction = async (args) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID and session claims - const { isAuthenticated, userId, sessionClaims } = await getAuth(args) - - // Protect the route by checking if the user is signed in - if (!isAuthenticated) { - return redirect('/sign-in?redirect_url=' + args.request.url) - } - - const fullName = sessionClaims.fullName - - const primaryEmail = sessionClaims.primaryEmail - - return { fullName, primaryEmail } - } - ``` - - For Tanstack React Start, the `Auth` object is accessed using the `getAuth()` function. [Learn more about using `getAuth()`](/docs/tanstack-react-start/guides/users/reading#server-side). ```ts {{ filename: 'src/routes/api/example.ts' }} - import { getAuth } from '@clerk/tanstack-react-start/server' + import { auth } from '@clerk/tanstack-react-start/server' import { json } from '@tanstack/react-start' - import { createServerFileRoute } from '@tanstack/react-start/server' + import { createFileRoute } from '@tanstack/react-router' - export const ServerRoute = createServerFileRoute().methods({ - GET: async ({ request, params }) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID and session claims - const { isAuthenticated, userId, sessionClaims } = await getAuth(request) + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + // Use `auth()` to access the user's session claims + const { isAuthenticated, sessionClaims } = await auth - // Protect the API route by checking if the user is signed in - if (!isAuthenticated) { - return json({ error: 'Unauthorized' }, { status: 401 }) - } + if (!isAuthenticated) { + return json({ error: 'Unauthorized' }, { status: 401 }) + } - const fullName = sessionClaims.fullName + const fullName = sessionClaims.fullName - const primaryEmail = sessionClaims.primaryEmail + const primaryEmail = sessionClaims.primaryEmail - return json({ fullName, primaryEmail }) + return json({ fullName, primaryEmail }) + }, + }, }, }) ``` diff --git a/docs/guides/sessions/force-token-refresh.mdx b/docs/guides/sessions/force-token-refresh.mdx index e787071693..9f63f7173f 100644 --- a/docs/guides/sessions/force-token-refresh.mdx +++ b/docs/guides/sessions/force-token-refresh.mdx @@ -48,17 +48,19 @@ To do so, follow the [guide on customizing your session token](/docs/guides/sess // Use `req.json()` to parse the request body const { birthday } = await req.json() - // Use `auth()` to access `isAuthenticated` and the current user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = await auth() - // Protect the route by checking if the user is signed in + // Protect the route from unauthenticated users if (!isAuthenticated) { return Response.json({ error: 'Unauthorized' }, { status: 401 }) } - // Use `clerkClient()` to access the Clerk client - // The Clerk client gives you access to Clerk Backend API resources, - // such as the `users` resource, which includes methods for managing users + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart const client = await clerkClient() // Use the JS Backend SDK's `updateUserMetadata()` method to update the user's birthday diff --git a/docs/guides/sessions/manual-jwt-verification.mdx b/docs/guides/sessions/manual-jwt-verification.mdx index 27cec70633..31bf9cfc16 100644 --- a/docs/guides/sessions/manual-jwt-verification.mdx +++ b/docs/guides/sessions/manual-jwt-verification.mdx @@ -11,9 +11,150 @@ For every request, you must validate the token to ensure it hasn't expired or be The [`authenticateRequest()`](/docs/reference/backend/authenticate-request) method from the JS Backend SDK accepts the `request` object and authenticates the session token in it. -The following example uses the `authenticateRequest()` method to verify the session token. It also performs networkless authentication by passing `jwtKey`. This verifies if the user is signed into the application. For more information, including usage with higher-level SDKs, see the [`authenticateRequest()` reference](/docs/reference/backend/authenticate-request). +The following example uses the `authenticateRequest()` method to verify the session token. _It also performs networkless authentication_ by passing `jwtKey`. This verifies if the user is signed into the application. - +`authenticateRequest()` requires `publishableKey` to be set. If you are importing `clerkClient` from a higher-level SDK, such as Next.js, then `clerkClient` infers the `publishableKey` from your [environment variables](/docs/guides/development/clerk-environment-variables#clerk-publishable-and-secret-keys). + + + + ```tsx {{ filename: 'app/api/example/route.ts' }} + import { clerkClient } from '@clerk/nextjs/server' + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + const client = await clerkClient() + + export async function GET(req: Request) { + // Use the `authenticateRequest()` method to verify the token + const { isAuthenticated } = await client.authenticateRequest(req, { + authorizedParties: ['https://example.com'], + jwtKey: process.env.CLERK_JWT_KEY, + }) + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return Response.json({ status: 401 }) + } + + // Add logic to perform protected actions + + return Response.json({ message: 'This is a reply' }) + } + ``` + + + + ```tsx {{ filename: 'src/api/example.ts' }} + import { clerkClient } from '@clerk/astro/server' + import type { APIRoute } from 'astro' + + export const GET: APIRoute = async (context) => { + // Use the `authenticateRequest()` method to verify the token + const { isAuthenticated } = await clerkClient(context).authenticateRequest(context.request, { + authorizedParties: ['https://example.com'], + jwtKey: process.env.CLERK_JWT_KEY, + }) + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return Response.json({ status: 401 }) + } + + // Add logic to perform protected actions + + return Response.json({ message: 'This is a reply' }) + } + ``` + + + + ```js {{ filename: 'index.js' }} + import { createClerkClient } from '@clerk/express' + import express from 'express' + + const app = express() + const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) + + app.get('/user', async (req, res) => { + const { isAuthenticated } = await clerkClient.authenticateRequest(req, { + authorizedParties: ['https://example.com'], + jwtKey: process.env.CLERK_JWT_KEY, + }) + + if (!isAuthenticated) { + res.status(401).json({ error: 'User not authenticated' }) + } + + // Add logic to perform protected actions + + // Return the Backend User object + res.json({ message: 'This is a reply' }) + }) + ``` + + + + If you are using the [JS Backend SDK](/docs/js-backend/getting-started/quickstart) on its own, you need to provide the `secretKey` and `publishableKey` to `createClerkClient()` so that it is passed to `authenticateRequest()`. You can set these values as [environment variables](/docs/guides/development/clerk-environment-variables#clerk-publishable-and-secret-keys) and then pass them to the function. + + + + + + ```tsx {{ filename: 'app/routes/example.tsx' }} + import { redirect } from 'react-router' + import { clerkClient } from '@clerk/react-router/server' + import type { Route } from './+types/example' + + export async function loader(args: Route.LoaderArgs) { + // Use the `authenticateRequest()` method to verify the token + const { isAuthenticated } = await clerkClient(args).authenticateRequest(args.request, { + authorizedParties: ['https://example.com'], + jwtKey: process.env.CLERK_JWT_KEY, + }) + + if (!isAuthenticated) { + return redirect('/sign-in?redirect_url=' + args.request.url) + } + + // Add logic to perform protected actions + + return Response.json({ message: 'This is a reply' }) + } + ``` + + + + For Tanstack React Start, the `Auth` object is accessed using the `getAuth()` function. [Learn more about using `getAuth()`](/docs/tanstack-react-start/guides/users/reading#server-side). + + ```tsx {{ filename: 'app/routes/api/example.tsx' }} + import { createFileRoute } from '@tanstack/react-router' + import { clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async ({ request }) => { + // Use the `authenticateRequest()` method to verify the token + const { isAuthenticated } = await clerkClient().authenticateRequest(request, { + authorizedParties: ['https://example.com'], + jwtKey: process.env.CLERK_JWT_KEY, + }) + + if (!isAuthenticated) { + return Response.json({ status: 401 }) + } + + // Add logic to perform protected actions + + return Response.json({ message: 'This is a reply' }) + }, + }, + }, + }) + ``` + + ## Manually verify a session token diff --git a/docs/guides/sessions/session-tokens.mdx b/docs/guides/sessions/session-tokens.mdx index 44e8bd19ac..25943f585a 100644 --- a/docs/guides/sessions/session-tokens.mdx +++ b/docs/guides/sessions/session-tokens.mdx @@ -176,14 +176,18 @@ You could store only the necessary data in the session token - for example, just Then, when you need to access the other metadata fields, you can fetch them using a separate API call from your backend. The following example uses the [`getUser()`](/docs/reference/backend/user/get-user) method to access the current user's [Backend `User` object](/docs/reference/backend/types/backend-user), which includes the `publicMetadata` field. - +**If your SDK isn't listed, you can use the comments in the example to help you adapt it to your SDK.** + + ```tsx {{ filename: 'app/api/example/route.ts' }} import { auth, clerkClient } from '@clerk/nextjs/server' export async function GET() { - // Use `auth()` to access `isAuthenticated` and the user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = await auth() // Protect the route by checking if the user is signed in @@ -191,9 +195,12 @@ Then, when you need to access the other metadata fields, you can fetch them usin return new NextResponse('Unauthorized', { status: 401 }) } + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart const client = await clerkClient() - // Use the JS Backend SDK's `getUser()` method to get the Backend User object + // Use the`getUser()` method to get the Backend User object const user = await client.users.getUser(userId) // Return the Backend User object @@ -210,7 +217,9 @@ Then, when you need to access the other metadata fields, you can fetch them usin import { clerkClient } from '@clerk/astro/server' export async function GET(context) { - // Use `locals.auth()` to access `isAuthenticated` and the user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = context.locals.auth() // Protect the route by checking if the user is signed in @@ -218,7 +227,10 @@ Then, when you need to access the other metadata fields, you can fetch them usin return new Response('Unauthorized', { status: 401 }) } - // Use the JS Backend SDK's `getUser()` method to get the Backend User object + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the `getUser()` method to get the Backend User object const user = await clerkClient(context).users.getUser(userId) // Return the Backend User object @@ -236,7 +248,9 @@ Then, when you need to access the other metadata fields, you can fetch them usin const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }) app.get('/user', async (req, res) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = getAuth(req) // Protect the route by checking if the user is signed in @@ -244,7 +258,10 @@ Then, when you need to access the other metadata fields, you can fetch them usin res.status(401).json({ error: 'User not authenticated' }) } - // Use the JS Backend SDK's `getUser()` method to get the Backend User object + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the `getUser()` method to get the Backend User object const user = await clerkClient.users.getUser(userId) // Return the Backend User object @@ -260,7 +277,9 @@ Then, when you need to access the other metadata fields, you can fetch them usin import type { Route } from './+types/profile' export async function loader(args: Route.LoaderArgs) { - // Use `getAuth()` to access `isAuthenticated` and the user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = await getAuth(args) // Protect the route by checking if the user is signed in @@ -268,7 +287,10 @@ Then, when you need to access the other metadata fields, you can fetch them usin return redirect('/sign-in?redirect_url=' + args.request.url) } - // Get the Backend User object + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the `getUser()` method to get the Backend User object const user = await clerkClient(args).users.getUser(userId) // Return the Backend User object @@ -279,86 +301,37 @@ Then, when you need to access the other metadata fields, you can fetch them usin ``` - - - ```tsx {{ filename: 'routes/profile.tsx' }} - import { LoaderFunction, redirect } from '@remix-run/node' - import { getAuth } from '@clerk/remix/ssr.server' - import { createClerkClient } from '@clerk/remix/api.server' - - export const loader: LoaderFunction = async (args) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID - const { isAuthenticated, userId } = await getAuth(args) - - // If there is no userId, then redirect to sign-in route - if (!isAuthenticated) { - return redirect('/sign-in?redirect_url=' + args.request.url) - } - - // Use the JS Backend SDK's `getUser()` method to get the Backend User object - const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser( - userId, - ) - - // Return the Backend User object - return { serialisedUser: JSON.stringify(user) } - } - ``` - - ```tsx {{ filename: 'routes/profile.tsx' }} - import { ActionFunction, redirect } from '@remix-run/node' - import { getAuth } from '@clerk/remix/ssr.server' - import { createClerkClient } from '@clerk/remix/api.server' - - export const action: ActionFunction = async (args) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID - const { isAuthenticated, userId } = await getAuth(args) - - // If there is no userId, then redirect to sign-in route - if (!isAuthenticated) { - return redirect('/sign-in?redirect_url=' + args.request.url) - } - - // Prepare the data for the mutation - const params = { firstName: 'John', lastName: 'Wicker' } - - // // Use the JS Backend SDK's `updateUser()` method to update the Backend User object - const updatedUser = await createClerkClient({ - secretKey: process.env.CLERK_SECRET_KEY, - }).users.updateUser(userId, params) - - // Return the updated user - return { serialisedUser: JSON.stringify(updatedUser) } - } - ``` - - - ```tsx {{ filename: 'app/routes/api/example.tsx' }} - import { createClerkClient } from '@clerk/backend' import { json } from '@tanstack/react-start' - import { createAPIFileRoute } from '@tanstack/react-start/api' - import { getAuth } from '@clerk/tanstack-react-start/server' - - export const Route = createAPIFileRoute('/api/example')({ - GET: async ({ request, params }) => { - // Use `getAuth()` to access `isAuthenticated` and the user's ID - const { isAuthenticated, userId } = await getAuth(req) - - // Protect the route by checking if the user is signed in - if (!isAuthenticated) { - return json({ error: 'Unauthorized' }, { status: 401 }) - } - - // Instantiate the JS Backend SDK - const clerkClient = createClerkClient({ secretKey: import.meta.env.CLERK_SECRET_KEY }) - - // Use the JS Backend SDK's `getUser()` method to get the Backend User object - const user = await clerkClient.users.getUser(userId) - - // Return the Backend User object - return json({ user }) + import { createFileRoute } from '@tanstack/react-router' + import { auth, clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = await auth() + + if (!isAuthenticated) { + return new Response('User not authenticated', { + status: 404, + }) + } + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + // Use the `getUser()` method to get the Backend User object + const user = await clerkClient().users.getUser(userId) + + // Return the Backend User object + return json({ user }) + }, + }, }, }) ``` diff --git a/docs/guides/users/extending.mdx b/docs/guides/users/extending.mdx index 6a5a841079..7aff45502d 100644 --- a/docs/guides/users/extending.mdx +++ b/docs/guides/users/extending.mdx @@ -19,319 +19,29 @@ There are three types of metadata: "public", "private", and "unsafe". ### Public metadata -Public metadata is accessible by both the frontend and the backend, but can only be set on the backend. This is useful for storing data that you want to expose to the frontend, but don't want the user to be able to modify. For example, you could store a user's birthday. +Public metadata can be read by both the frontend and the backend, but can only be set on the backend. This is useful for storing data that you want to expose to the frontend, but don't want the user to be able to modify. For example, you could store a user's birthday. -#### Set public metadata - - - - - - ```ts {{ filename: 'route.ts' }} - export async function POST(req) { - const { birthday, userId } = await req.json() - - await clerkClient.users.updateUserMetadata(userId, { - publicMetadata: { - birthday, - }, - }) - - return Response.json({ success: true }) - } - ``` - - - If you're using Next.js, you must `await` the instantiation of the `clerkClient` instance, like so: - - ```ts - const client = await clerkClient() - - const response = await client.users.updateUserMetadata() - ``` - - - - - ```ts {{ filename: 'public.ts' }} - import { clerkClient } from '@clerk/express' - - app.post('/updateBirthday', async (req, res) => { - const { birthday, userId } = req.body - - await clerkClient.users.updateUserMetadata(userId, { - publicMetadata: { - birthday, - }, - }) - res.status(200).json({ success: true }) - }) - ``` - - - - ```go {{ filename: 'public.go' }} - var client clerk.Client - - func addStripeCustomerID(user *clerk.User, birthday string) error { - Role := map[string]interface{}{ - "birthday": birthday, - } - user, err := s.clerkClient.Users().UpdateMetadata(sess.UserID, &clerk.updateMetadataRequest{ - PublicMetadata: birthday, - }) - - if err != nil { - panic(err) - } - } - ``` - - - - ```ruby {{ filename: 'public.rb' }} - # ruby json example with a private metadata and stripe id - require 'clerk' - require 'json' - - birthday = { - "birthday": "1990-01-01", - } - - clerk = Clerk::SDK.new(api_key: "your_clerk_secret_key") - clerk.users.updateMetadata("user_xyz", public_metadata: birthday) - ``` - - - - ```bash {{ filename: 'curl.sh' }} - curl -XPATCH -H 'Authorization: Bearer CLERK_SECRET_KEY' -H "Content-type: application/json" -d '{ - "public_metadata": { - "birthday": "1990-01-01" - } - }' 'https://api.clerk.com/v1/users/{user_id}/metadata' - ``` - - +#### Access public metadata -#### Retrieve public metadata +To access public metadata on the frontend, it's available on the [`User`](/docs/reference/javascript/user) object, which can be accessed using the [`useUser()`](/docs/reference/hooks/use-user) hook. -There are multiple ways to retrieve public metadata. +To access public metadata on the backend, it's available on the [Backend `User`](/docs/reference/backend/types/backend-user) object which can be accessed using the [`getUser()`](/docs/reference/backend/user/get-user) method. This method will return the `User` object which contains the public metadata. However, this method is subject to [rate limits](/docs/guides/how-clerk-works/system-limits#backend-api-requests), so _if you are accessing the metadata frequently_, it's recommended to [attach it to the user's session token](#metadata-in-the-session-token). -On the frontend, it's available on the [`User`](/docs/reference/javascript/user) object, which can be accessed using the [`useUser()`](/docs/reference/hooks/use-user) hook. +#### Set public metadata -On the backend, it's available on the [Backend `User`](/docs/reference/backend/types/backend-user) object which can be accessed using the JS Backend SDK's [`getUser()`](/docs/reference/backend/user/get-user) method. This method will return the `User` object which contains the public metadata. However, this method is subject to [rate limits](/docs/guides/how-clerk-works/system-limits#backend-api-requests), so _if you are retrieving the metadata frequently_, it's recommended to [attach it to the user's session token](#metadata-in-the-session-token). +To set public metadata, see the [`updateUserMetadata()`](/docs/reference/backend/user/update-user-metadata) reference. ### Private metadata -Private metadata is only accessible by the backend and webhook handlers, which makes this useful for storing sensitive data that you don't want to expose to the frontend. For example, you could store a user's Stripe customer ID. - -#### Set private metadata - - - - - - ```ts {{ filename: 'route.ts' }} - export async function POST(req) { - const { stripeId, userId } = await req.json() - - await clerkClient.users.updateUserMetadata(userId, { - privateMetadata: { - stripeId: stripeId, - }, - }) - - return Response.json({ success: true }) - } - ``` - - - If you're using Next.js, you must `await` the instantiation of the `clerkClient` instance, like so: - - ```ts - const client = await clerkClient() - - const response = await client.users.updateUserMetadata() - ``` - - - - - ```ts {{ filename: 'private.ts' }} - import { clerkClient } from '@clerk/express' - - app.post('/updateStripe', async (req, res) => { - const { stripeId, userId } = req.body - - await clerkClient.users.updateUserMetadata(userId, { - privateMetadata: { - stripeId: stripeId, - }, - }) - - res.status(200).json({ success: true }) - }) - ``` - - - - ```go {{ filename: 'private.go' }} - import ( - "context" - "encoding/json" - - "github.com/clerk/clerk-sdk-go/v2/user" - "github.com/clerk/clerk-sdk-go/v2" - ) - - func addStripeCustomerID(userId string, stripeCustomerId string) (*clerk.User, error) { - ctx := context.Background() - - metadata := map[string]any{"stripe_id": stripeCustomerId} - - metadataJSON, err := json.Marshal(metadata) - - if err != nil { - return nil, err - } - - rawMessage := json.RawMessage(metadataJSON) - - updatedUser, err := user.UpdateMetadata(ctx, userId, &user.UpdateMetadataParams{ - PrivateMetadata: &rawMessage, - }) - - if err != nil { - return nil, err - } - - return updatedUser, nil - } - ``` - - - - ```ruby {{ filename: 'private.rb' }} - # ruby json example with a private metadata and stripe id - require 'clerk' - require 'json' - - privateMetadata = { - "stripeID": stripeCustomerID - } - - - clerk = Clerk::SDK.new(api_key: "your_clerk_secret_key") - clerk.users.updateMetadata("user_xyz", private_metadata: privateMetadata) - ``` - - - - ```bash {{ filename: 'curl.sh' }} - curl -XPATCH -H 'Authorization: Bearer CLERK_SECRET_KEY' -H "Content-type: application/json" -d '{ - "private_metadata": { - "stripeId": "12356" - } - }' 'https://api.clerk.com/v1/users/{user_id}/metadata' - ``` - - - -#### Retrieve private metadata - -You can retrieve the private metadata for a user by using the JS Backend SDK's [`getUser()`](/docs/reference/backend/user/get-user) method. This method will return the `User` object which contains the private metadata. However, this method is subject to [rate limits](/docs/guides/how-clerk-works/system-limits#backend-api-requests), so if you are retrieving the metadata frequently, it's recommended to [attach it to the user's session token](#metadata-in-the-session-token). - - - - - - ```ts {{ filename: 'route.ts' }} - export async function GET(req) { - const { userId } = await req.json() - - const user = await clerkClient.users.getUser(userId) - - return Response.json(user.privateMetadata) - } - ``` - - - If you're using Next.js, you must `await` the instantiation of the `clerkClient` instance, like so: - - ```ts - const client = await clerkClient() - - const response = await client.users.getUser() - ``` - - - - - ```ts {{ filename: 'private.ts' }} - import { clerkClient } from '@clerk/express' - - app.post('/updateStripe', async (req, res) => { - const { userId } = req.body - - const user = await clerkClient.users.getUser(userId) - - res.status(200).json(user.privateMetadata) - }) - ``` - - - - ```go {{ filename: 'private.go' }} - import ( - "context" - "encoding/json" - - "github.com/clerk/clerk-sdk-go/v2/user" - "github.com/clerk/clerk-sdk-go/v2" - ) +Private metadata can only be read and set by the backend and webhook handlers, which makes this useful for storing sensitive data that you don't want to expose to the frontend. For example, you could store a user's Stripe customer ID. - type PrivateMetadata struct { - StripeID string `json:"stripe_id"` - } +#### Access private metadata - func getPrivateMetadata(userId string) (*PrivateMetadata, error) { - ctx := context.Background() +To access private metadata on the backend, it's available on the [Backend `User`](/docs/reference/backend/types/backend-user) object which can be accessed using the [`getUser()`](/docs/reference/backend/user/get-user) method. This method will return the `User` object which contains the private metadata. However, this method is subject to [rate limits](/docs/guides/how-clerk-works/system-limits#backend-api-requests), so _if you are accessing the metadata frequently_, it's recommended to [attach it to the user's session token](#metadata-in-the-session-token). - clerkUser, err := user.Get(ctx, userId) - - if err != nil { - return nil, err - } - - privateMetadata := &PrivateMetadata{} - - err = json.Unmarshal(clerkUser.PrivateMetadata, privateMetadata) - - if err != nil { - return nil, err - } - - return privateMetadata, nil - } - ``` - - - - ```ruby {{ filename: 'private.rb' }} - # ruby json example with a private metadata and stripe id - require 'clerk' - clerk = Clerk::SDK.new(api_key: "your_clerk_secret_key") - clerk.users.getUser("user_xyz") - ``` - +#### Set private metadata - - ```bash {{ filename: 'curl.sh' }} - curl -XGET -H 'Authorization: CLERK_SECRET_KEY' -H "Content-type: application/json" 'https://api.clerk.com/v1/users/{user_id}' - ``` - - +To set private metadata, see the [`updateUserMetadata()`](/docs/reference/backend/user/update-user-metadata) reference. ### Unsafe metadata @@ -339,127 +49,21 @@ Unsafe metadata can be both read and set from the frontend and the backend. It's Unsafe metadata is the only metadata property that can be set during sign-up, so a common use case is to use it in [custom onboarding flows](/docs/guides/development/add-onboarding-flow). Custom data collected during the onboarding (sign-up) flow can be stored in the [`SignUp`](/docs/reference/javascript/sign-up) object. After a successful sign-up, `SignUp.unsafeMetadata` is copied to the `User` object as `User.unsafeMetadata`. From that point on, the unsafe metadata is accessible as a direct attribute of the `User` object. -#### Set unsafe metadata - -The following examples demonstrate how to update unsafe metadata for an existing user. Updating `unsafeMetadata` replaces the previous value; it doesn't perform a merge. To merge data, you can pass a combined object such as `{ …user.unsafeMetadata, …newData }` to the `unsafeMetadata` parameter. - -The following examples demonstrate how to update `unsafeMetadata` on the server-side versus the client-side. - - - - - - - - ```ts {{ filename: 'route.ts' }} - export async function POST(req) { - const { userId } = await req.json() - - await clerkClient.users.updateUserMetadata(userId, { - unsafeMetadata: { - birthday: '11-30-1969', - }, - }) - - return Response.json({ success: true }) - } - ``` - - - If you're using Next.js, you must `await` the instantiation of the `clerkClient` instance, like so: - - ```ts - const client = await clerkClient() - - const response = await client.users.updateUserMetadata() - ``` - - - - - ```ts {{ filename: 'private.ts' }} - import { clerkClient } from '@clerk/express' - - app.post('/updateStripe', async (req, res) => { - const { stripeId, userId } = await req.body - - await clerkClient.users.updateUserMetadata(userId, { - unsafeMetadata: { - birthday: '11-30-1969', - }, - }) - - res.status(200).json({ success: true }) - }) - ``` - - - - ```go {{ filename: 'private.go' }} - import ( - "context" - "encoding/json" - - "github.com/clerk/clerk-sdk-go/v2/user" - "github.com/clerk/clerk-sdk-go/v2" - ) - - func addBirthdayToUser(userId string, birthday string) (*clerk.User, error) { - ctx := context.Background() - - metadata := map[string]any{"birthday": birthday} - - metadataJSON, err := json.Marshal(metadata) - - if err != nil { - return nil, err - } - - rawMessage := json.RawMessage(metadataJSON) - - updatedUser, err := user.UpdateMetadata(ctx, userId, &user.UpdateMetadataParams{ - UnsafeMetadata: &rawMessage, - }) - - if err != nil { - return nil, err - } - - return updatedUser, nil - } - ``` - +#### Access unsafe metadata - - ```ruby {{ filename: 'private.rb' }} - require 'clerk' - require 'json' +To access unsafe metadata on the frontend, it's available on the [`User`](/docs/reference/javascript/user) object, which can be accessed using the [`useUser()`](/docs/reference/hooks/use-user) hook. - unsafeMetadata = { - "birthday": "04-20-1969" - } +To access unsafe metadata on the backend, it's available on the [Backend `User`](/docs/reference/backend/types/backend-user) object which can be accessed using the [`getUser()`](/docs/reference/backend/user/get-user) method. This method will return the `User` object which contains the unsafe metadata. However, this method is subject to [rate limits](/docs/guides/how-clerk-works/system-limits#backend-api-requests), so _if you are accessing the metadata frequently_, it's recommended to [attach it to the user's session token](#metadata-in-the-session-token). - clerk = Clerk::SDK.new(api_key: "your_clerk_secret_key") - clerk.users.updateMetadata("user_123", unsafe_metadata: unsafeMetadata) - ``` - +#### Set unsafe metadata - - ```bash {{ filename: 'curl.sh' }} - curl -XPATCH -H 'Authorization: Bearer CLERK_SECRET_KEY' -H "Content-type: application/json" -d '{ - "unsafe_metadata": { - "birthday": "11-30-1969" - } - }' 'https://api.clerk.com/v1/users/{user_id}/metadata' - ``` - - - +See the following examples to see how to set unsafe metadata on the frontend (client-side) and backend (server-side). + - For React-based SDKs, such as Next.js, Tanstack React Start, and React Router, use the [`useUser()`](/docs/reference/hooks/use-user) hook to update unsafe metadata. + For React-based SDKs, such as Next.js, use the [`useUser()`](/docs/reference/hooks/use-user) hook to update unsafe metadata. ```tsx {{ filename: 'page.tsx' }} export default function Page() { @@ -519,15 +123,11 @@ The following examples demonstrate how to update `unsafeMetadata` on the server- - - -#### Retrieve unsafe metadata - -There are multiple ways to retrieve unsafe metadata. -On the frontend, it's available on the [`User`](/docs/reference/javascript/user) object, which can be accessed using the [`useUser()`](/docs/reference/hooks/use-user) hook. - -On the backend, it's available on the [Backend `User`](/docs/reference/backend/types/backend-user) object which can be accessed using the JS Backend SDK's [`getUser()`](/docs/reference/backend/user/get-user) method. This method will return the `User` object which contains the unsafe metadata. However, this method is subject to [rate limits](/docs/guides/how-clerk-works/system-limits#backend-api-requests), so if you are retrieving the metadata frequently, it's recommended to [attach it to the user's session token](#metadata-in-the-session-token). + + To update unsafe metadata on the server-side, see the [`updateUserMetadata()`](/docs/reference/backend/user/update-user-metadata) reference. + + ## Metadata in the session token diff --git a/docs/guides/users/impersonation.mdx b/docs/guides/users/impersonation.mdx index b94050f3ac..3b3f42de89 100644 --- a/docs/guides/users/impersonation.mdx +++ b/docs/guides/users/impersonation.mdx @@ -30,7 +30,7 @@ Actor tokens are similar to [sign-in tokens](/docs/reference/backend-api/tag/sig To create an actor token, you can use the [Backend API](/docs/reference/backend-api/tag/actor-tokens/post/actor_tokens){{ target: '_blank' }}. -The following example demonstrates how to create an actor token that is valid for 600 seconds (10 minutes). Using the generated token will result in user with ID `user_21Ufcy98STcA11s3QckIwtwHIES` (the actor/impersonator) signing in as user with ID `user_1o4qfak5AdI2qlXSXENGL05iei6` (the subject/impersonated). +The following example demonstrates how to create an actor token by making a request directly to Clerk's Backend API using cURL. Using the generated token will result in user with ID `user_21Ufcy98STcA11s3QckIwtwHIES` (the actor/impersonator) signing in as user with ID `user_1o4qfak5AdI2qlXSXENGL05iei6` (the subject/impersonated). ```bash curl -X POST https://api.clerk.com/v1/actor_tokens -d '{ \ @@ -50,7 +50,7 @@ To revoke an actor token, you can use the [Backend API](/docs/reference/backend- Despite it's expiration date, you can revoke a token at any time. Revoked actor tokens can no longer be used for signing in. -The following example demonstrates how to revoke an actor token, even if it's not expired yet. +The following example demonstrates how to revoke an actor token by making a request directly to Clerk's Backend API using cURL, even if it's not expired yet. ```bash curl -X POST https://api.clerk.com/v1/actor_tokens/act_2EL6mQKzeUtoRwGuLZsznyfkIsH/revoke @@ -79,7 +79,7 @@ Clerk also adds an `act` claim on the [Clerk session token](/docs/guides/session To detect impersonated sessions in the frontend, the `actor` object contains the `sub` claim of the impersonator. You can use this information to detect impersonated sessions. - + You can use the [`useAuth()`](/docs/reference/hooks/use-auth) hook to get access to the authentication context, which includes the `actor` object. @@ -126,9 +126,9 @@ To detect impersonated sessions in the frontend, the `actor` object contains the ### Detect impersonated sessions in the backend -The [`Auth`](/docs/reference/backend/types/auth-object) object is a server-side object that contains the `actor` object, as well as important information like the current user's session ID and user ID. Accessing the `Auth` object differs [depending on the SDK you're using](/docs/reference/backend/types/auth-object#how-to-access-the-auth-object). Here are some examples: +The [`Auth`](/docs/reference/backend/types/auth-object) object is a server-side object that contains the `actor` object, as well as important information like the current user's session ID and user ID. **Accessing the `Auth` object differs [depending on the SDK you're using](/docs/reference/backend/types/auth-object#how-to-access-the-auth-object).** Here are some examples: - + The Next.js SDK provides the [`auth()`](/docs/reference/nextjs/app-router/auth) helper to App Router apps to access the `Auth` object. @@ -136,8 +136,8 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object is a server-side import { auth } from '@clerk/nextjs/server' export default async function Page() { - // Use Clerk's `auth()` helper to access the `Auth` object - const { userId, actor } = await auth() + // Use `auth()` to access the `Auth` object + const { actor, userId } = await auth() return (
@@ -152,6 +152,27 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object is a server-side ``` + + The Astro SDK provides the `locals.auth()` function to access the `Auth` object. [Learn more about using `locals.auth()`](/docs/astro/guides/users/reading#server-side). + + ```astro {{ filename: 'src/pages/example.astro' }} + --- + // Use `locals.auth()` to access the `Auth` object + const { actor, userId } = Astro.locals.auth() + --- + +
+ { + actor && ( + + user {actor.sub} has signed in as user {userId || ''} + + ) + } +
+ ``` +
+ The Express SDK provides a [middleware](/docs/reference/express/overview#clerk-middleware) that attaches the [`Auth`](/docs/reference/backend/types/auth-object) object to the `request` object. @@ -175,4 +196,59 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object is a server-side }) ``` + + + The React Router SDK provides the [`getAuth()`](/docs/reference/react-router/get-auth) function to access the `Auth` object. + + ```tsx {{ filename: 'app/routes/example.tsx' }} + import { redirect } from 'react-router' + import { clerkClient, getAuth } from '@clerk/react-router/server' + import type { Route } from './+types/example' + + export async function loader(args: Route.LoaderArgs) { + // Use `getAuth()` to access the `Auth` object + const { isAuthenticated, actor, userId } = await getAuth(args) + + return { + actor: actor, + userId: userId || '', + } + } + + export default function Example({ loaderData }: Route.ComponentProps) { + return ( +
+ {loaderData.actor && ( + + user {loaderData.actor.sub} has signed in as user {loaderData.userId} + + )} +
+ ) + } + ``` +
+ + + The Tanstack React Start SDK provides the `auth()` function to access the `Auth` object. [Learn more about using `auth()`](/docs/tanstack-react-start/guides/users/reading#server-side). + + ```tsx {{ filename: 'src/routes/api/example.tsx' }} + import { auth } from '@clerk/tanstack-react-start/server' + import { json } from '@tanstack/react-start' + import { createFileRoute } from '@tanstack/react-router' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + // Use `auth()` to access the `Auth` object + const { actor, userId } = await auth() + + return json({ actor, userId }) + }, + }, + }, + }) + ``` + diff --git a/docs/guides/users/inviting.mdx b/docs/guides/users/inviting.mdx index 4629e9aa21..13426a4f44 100644 --- a/docs/guides/users/inviting.mdx +++ b/docs/guides/users/inviting.mdx @@ -5,7 +5,7 @@ description: Learn how to invite users to your Clerk application. Inviting users to your Clerk application allows you to onboard new users seamlessly by sending them a unique invitation link. -Once you create an invitation, Clerk sends an email to the invited user with a unique invitation link. When the user visits the invitation link, they will be redirected to the [Account Portal sign-up page](/docs/guides/customizing-clerk/account-portal#sign-up) and **their email address will be automatically verified.** If you want to redirect the user to a specific page in your application, you can [specify a redirect URL when creating the invitation](#redirect-url). +Once you create an invitation, Clerk sends an email to the invited user with a unique invitation link. When the user visits the invitation link, they will be redirected to the [Account Portal sign-up page](/docs/guides/customizing-clerk/account-portal#sign-up) and **their email address will be automatically verified.** If you want to redirect the user to a specific page in your application, you can [specify a redirect URL when creating the invitation](#with-a-redirect-url). Invitations expire after a month. If the user clicks on an expired invitation, they will get redirected to the application's sign-up page and will have to go through the normal sign-up flow. Their email address will not be auto-verified. @@ -14,122 +14,57 @@ Invitations expire after a month. If the user clicks on an expired invitation, t ## Create an invitation -You can create an invitation in the [Clerk Dashboard](#using-clerk-dashboard) or [using the Backend API](#using-backend-api). +You can create an invitation either in the [Clerk Dashboard](#in-the-clerk-dashboard) or [programmatically](#programmatically). When making this decision, keep in mind that if you create an invitation through the Clerk Dashboard, you can only set an invitation expiration date. If you create an invitation programatically, you are able to set more options, such as the URL you want the user to be redirected to after they accept the invitation, metadata to add to the invitation, or whether an invitation should be created if there is already an existing invitation for the given email address. -### Using Clerk Dashboard +### In the Clerk Dashboard -To invite users, navigate to the **Users** page from the top-level menu, then select the **Invitations** tab. Select **Invite user** and enter the email address of the person you want to invite. Optionally, set an expiration date for the invitation. Once you've entered the necessary details, select **Send invite** to send the invitation email. +To create an invitation in the Clerk Dashboard, navigate to the [**Invitations**](https://dashboard.clerk.com/~/users/invitations) page. -> [!TIP] -> The same page will be available through the top-level menu, when you are in the **Restricted** sign-up mode. - -### Using Backend API - -You can either use a cURL command or the [JS Backend SDK](/docs/js-backend/getting-started/quickstart) to create an invitation. Clerk's JS Backend SDK is a wrapper around the Backend API that makes it easier to interact with the API. Use the following tabs to see examples for each method. - - - - The following example demonstrates how to create an invitation using cURL. - - - Replace the email address with the email address you want to invite. Your Clerk Secret Key is already injected into the code snippet. - - - - Replace the email address with the email address you want to invite. Update `YOUR_SECRET_KEY` with your Clerk Secret Key which can be found on the [**API keys**](https://dashboard.clerk.com/~/api-keys) page in the Clerk Dashboard. - +### Programmatically - ```bash {{ filename: 'terminal' }} - curl https://api.clerk.com/v1/invitations -X POST -d '{"email_address": "email@example.com"}' -H "Authorization:Bearer {{secret}}" -H 'Content-Type:application/json' - ``` - +To create an invitation programmatically, you can either [make a request directly to Clerk's Backend API](/docs/reference/backend/invitations/create-invitation#backend-api-bapi-endpoint) or use the [`createInvitation()`](/docs/reference/backend/invitations/create-invitation) method as shown in the following example. - - To use the JS Backend SDK to create an invitation, see the [`createInvitation()` reference](/docs/reference/backend/invitations/create-invitation). - - + See the [Backend API reference](/docs/reference/backend-api/tag/invitations/post/invitations){{ target: '_blank' }} for an example of the response. -### Redirect URL +### With a redirect URL -When you create an invitation, you can specify a `redirect_url` parameter. This parameter tells Clerk where to redirect the user when they visit the invitation link. - -The following example demonstrates how to use cURL to create an invitation with the `redirect_url` set to `https://www.example.com/accept-invitation`: +> [!WARNING] +> You currently cannot specify a redirect URL when creating an invitation in the Clerk Dashboard; if you need to specify a redirect URL, you need to create the invitation programmatically. -```bash -curl https://api.clerk.com/v1/invitations -X POST -d '{"email_address": "email@example.com", "redirect_url": "https://www.example.com/accept-invitation"}' -H "Authorization:Bearer {{secret}}" -H 'Content-Type:application/json' -``` +When you create an invitation programmatically, you can specify a `redirectUrl` parameter. This parameter tells Clerk where to redirect the user when they visit the invitation link. -Once the user visits the invitation link, they will be redirected to the page you specified, which means you must handle the sign-up flow in your code for that page. You can either embed the [``](/docs/reference/components/authentication/sign-up) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can build a [custom flow](/docs/guides/development/custom-flows/authentication/application-invitations). +Once the user visits the invitation link, they will be redirected to the page you specified, which means **you must handle the sign-up flow in your code for that page**. You can either embed the [``](/docs/reference/components/authentication/sign-up) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can build a [custom flow](/docs/guides/development/custom-flows/authentication/application-invitations). > [!TIP] > > - To test redirect URLs in your development environment, pass your port (e.g. `http://localhost:3000`). > - To use the Account Portal, pass the URL provided by Clerk on the [**Account Portal**](https://dashboard.clerk.com/~/account-portal) page in the Clerk Dashboard. For example, `https://prepared-phoenix-98.accounts.dev/sign-up` redirects the user to the Account Portal sign-up page. -### Invitation metadata - -You can also add metadata to an invitation. Once the invited user signs up using the invitation link, the invitation metadata will end up in the user's `public_metadata`. You can find more information about user metadata in the [metadata](/docs/guides/users/extending) docs. - -To add metadata to an invitation, you can use the `public_metadata` property when the invitation is created. - -The following example demonstrates how to create an invitation with metadata using cURL. - - - Replace the email address with the email address you want to invite, and the `public_metadata` with the metadata you want to add. Your Secret Key is already injected into the code snippet. - +### With invitation metadata - - Replace the email address with the email address you want to invite, and the `public_metadata` with the metadata you want to add. Update `YOUR_SECRET_KEY` with your Clerk Secret Key which can be found on the [**API keys**](https://dashboard.clerk.com/~/api-keys) page in the Clerk Dashboard. - +When you create an invitation programmatically, you can specify a `publicMetadata` parameter to add metadata to an invitation. Once the invited user signs up using the invitation link, the invitation metadata will end up in the user's public metadata. [Learn more about user metadata](/docs/guides/users/extending). -```bash -curl https://api.clerk.com/v1/invitations -X POST -d '{"email_address": "email@example.com", "public_metadata": {"age": "21"}}' -H "Authorization:Bearer {{secret}}" -H 'Content-Type:application/json' -``` +## Revoke an invitation -## Revoking invitations +You can revoke an invitation at any time. Revoking an invitation prevents the user from using the invitation link that was sent to them. You can revoke an invitation in the [Clerk Dashboard](#in-the-clerk-dashboard-2) or [programmatically](#programmatically-2). -You can revoke an invitation at any time. Revoking an invitation prevents the user from using the invitation link that was sent to them. You can revoke an invitation in the [Clerk Dashboard](#using-clerk-dashboard-2) or [using the Backend API](#using-backend-api-2). - -### Using Clerk Dashboard - -To revoke an invitation, navigate to the **Users** page from the top-level menu, then select the **Invitations** tab. Find the invitation you want to revoke and select **Revoke** from the dropdown menu on the right. - -> [!TIP] -> The same page will be available through the top-level menu, when you are in the **Restricted** sign-up mode. - -### Using Backend API - -You can either use a cURL command or the [JS Backend SDK](/docs/js-backend/getting-started/quickstart) to revoke an invitation. Use the following tabs to see examples for each method. +> [!WARNING] +> Revoking an invitation does **not** prevent the user from signing up on their own. If you're looking to restrict access to invited users only, refer to the [**Restricted** sign-up mode](/docs/guides/secure/restricting-access#sign-up-modes). - - - The following example demonstrates how to revoke an invitation using cURL. +### In the Clerk Dashboard - - Replace the `` with the ID of the invitation you want to revoke. Your Secret Key is already injected into the code snippet. - +To revoke an invitation in the Clerk Dashboard, navigate to the [**Invitations**](https://dashboard.clerk.com/~/users/invitations) page. - - Replace the `` with the ID of the invitation you want to revoke and replace `YOUR_SECRET_KEY` with your Clerk Secret Key. You can find your Secret Key on the [**API keys**](https://dashboard.clerk.com/~/api-keys) page in the Clerk Dashboard. - +### Programmatically - ```bash {{ filename: 'terminal' }} - curl https://api.clerk.com/v1/invitations//revoke -X POST -H "Authorization:Bearer {{secret}}" -H 'Content-Type:application/json' - ``` - +To revoke an invitation programmatically, you can either [make a request directly to Clerk's Backend API](/docs/reference/backend/invitations/revoke-invitation#backend-api-bapi-endpoint) or use the [`revokeInvitation()`](/docs/reference/backend/invitations/revoke-invitation) method as shown in the following example. - - To use the JS Backend SDK to revoke an invitation, see the [`revokeInvitation()`](/docs/reference/backend/invitations/revoke-invitation) reference documentation. - - + See the [Backend API reference](/docs/reference/backend-api/tag/invitations/post/invitations/\{invitation_id}/revoke){{ target: '_blank' }} for an example of the response. -> [!WARNING] -> Revoking an invitation does **not** prevent the user from signing up on their own. If you're looking to restrict access to invited users only, refer to the [**Restricted** sign-up mode](/docs/guides/secure/restricting-access#sign-up-modes). - ## Custom flow Clerk's [prebuilt components](/docs/reference/components/overview) and [Account Portal pages](/docs/guides/customizing-clerk/account-portal) handle the sign-up flow for you, including the invitation flow. If Clerk's prebuilt components don't meet your specific needs or if you require more control over the logic, you can rebuild the existing Clerk flows using the Clerk API. For more information, see the [custom flow for application invitations](/docs/guides/development/custom-flows/authentication/application-invitations). diff --git a/docs/guides/users/managing.mdx b/docs/guides/users/managing.mdx index 08411300bf..e817f5365d 100644 --- a/docs/guides/users/managing.mdx +++ b/docs/guides/users/managing.mdx @@ -11,15 +11,15 @@ For more information on the `User` object, such as helper methods for retrieving ## Manage users -You can manage your users [in the Clerk Dashboard](#in-the-clerk-dashboard), or [in your app](#in-your-app). +You can manage your users [in the Clerk Dashboard](#in-the-clerk-dashboard), or [programmatically](#programmatically). ### In the Clerk Dashboard To manage users in the Clerk Dashboard, navigate to the [**Users**](https://dashboard.clerk.com/~/users) page. -### In your app +### Programmatically -You can manage users in your app either through the [frontend](#in-the-frontend) or [backend](#in-the-backend). +You can manage users programmatically through the [frontend](#in-the-frontend) or [backend](#in-the-backend). #### In the frontend @@ -31,165 +31,32 @@ Depending on the level of abstraction you need, you can manage users in the fron #### In the backend -The [JS Backend SDK](/docs/js-backend/getting-started/quickstart) exposes the [Backend API](/docs/reference/backend-api){{ target: '_blank' }} resources and low-level authentication utilities for JavaScript environments. - -There are many operations available for managing users, such as `getUser()`, `createUser()`, and `deleteUser()`. For more information, see the [JS Backend reference docs](/docs/js-backend/getting-started/quickstart). +The [JS Backend SDK](/docs/js-backend/getting-started/quickstart) is a wrapper around the [Backend API](/docs/reference/backend-api){{ target: '_blank' }} that makes it easier to interact with the API. It includes many methods for managing users, such as `getUser()`, `createUser()`, and `deleteUser()`. For more information, see the [JS Backend SDK reference docs](/docs/js-backend/getting-started/quickstart). ## Create users -There are two ways to create users in Clerk: [in the Clerk Dashboard](#in-the-clerk-dashboard) or [using the Backend API](#using-the-backend-api). - -### In the Clerk Dashboard - -To create users in the Clerk Dashboard: - -1. In the top in the Clerk Dashboard, select [**Users**](https://dashboard.clerk.com/~/users). -1. Select **Create user**. -1. Enter the required user details and select **Create**. - -### Using the Backend API - -You can create users in your app using Clerk's Backend API. - -Use the following tabs to see examples of how to create users using one of the following: - -- JS Backend SDK -- Express -- cURL - - - - The following example shows how to create a user using the JS Backend SDK's [`createUser()`](/docs/reference/backend/user/create-user) method from the `users` sub-api of the `clerkClient` instance. - - - - ```ts {{ filename: 'route.ts' }} - export async function POST() { - try { - const user = await clerkClient.users.createUser({ - emailAddress: ['test@example.com'], - password: 'password', - }) - return Response.json({ message: 'User created', user }) - } catch (error) { - console.log(error) - return Response.json({ error: 'Error creating user' }) - } - } - ``` - - - If you're using Next.js, you must `await` the instantiation of the `clerkClient` instance, like so: - - ```ts - const client = await clerkClient() - - const response = await client.users.createUser() - ``` - - - - - ```ts {{ filename: 'create-user.ts' }} - import { clerkClient } from '@clerk/express' - - app.post('/createUser', async (req, res) => { - const userData = req.body - - try { - const user = await clerkClient.users.createUser(userData) - res.status(200).json({ message: 'User created', user }) - } catch (error) { - console.log(error) - res.status(500).json({ error: 'Error creating user' }) - } - }) - ``` - - - - ```bash {{ filename: 'terminal' }} - curl 'https://api.clerk.com/v1/users' -X POST -H 'Authorization:Bearer {{secret}}' -H 'Content-Type:application/json' -d '{ - "email_address": ["test@example.com"], - "password": "my-secure-password" - }' - ``` - - - -## Delete users - -There are two ways to delete users in Clerk: [in the Clerk Dashboard](#in-the-clerk-dashboard) or [using the Backend API](#using-the-backend-api). +You can create users either [in the Clerk Dashboard](#in-the-clerk-dashboard-2) or [programmatically](#programmatically-2). ### In the Clerk Dashboard -To delete users in the Clerk Dashboard: - -1. At the top of the Clerk Dashboard, select [**Users**](https://dashboard.clerk.com/~/users). -1. You can either select the user and then in the side navigation menu, select **Delete user**, or select the menu icon on the right side of the user's row and select **Delete user**. +To create a user in the Clerk Dashboard, navigate to the [**Users**](https://dashboard.clerk.com/~/users) page and select **Create user**. -### Using the Backend API +### Programmatically -You can delete users in your app using Clerk's Backend API. +To create a user programmatically, you can either [make a request directly to Clerk's Backend API](/docs/reference/backend/user/create-user#backend-api-bapi-endpoint) or use the [`createUser()`](/docs/reference/backend/user/create-user) method as shown in the following example. -Use the following tabs to see examples of how to delete users using one of the following: + -- JS Backend SDK -- Express -- cURL - - - - The following example shows how to delete a user using the JS Backend SDK's [`deleteUser()`](/docs/reference/backend/user/delete-user) method from the `users` sub-api of the `clerkClient` instance. - - - - ```ts {{ filename: 'route.ts' }} - export async function DELETE() { - const userId = 'user_123' - - try { - await clerkClient.users.deleteUser(userId) - return Response.json({ message: 'User deleted' }) - } catch (error) { - console.log(error) - return Response.json({ error: 'Error deleting user' }) - } - } - ``` - - - If you're using Next.js, you must `await` the instantiation of the `clerkClient` instance, like so: +## Delete users - ```ts - const client = await clerkClient() +You can delete users either [in the Clerk Dashboard](#in-the-clerk-dashboard-3) or [programmatically](#programmatically-3). - const response = await client.users.deleteUser(userId) - ``` - - +### In the Clerk Dashboard - - ```ts {{ filename: 'delete-user.ts' }} - import { clerkClient } from '@clerk/express' +To delete a user in the Clerk Dashboard, navigate to the [**Users**](https://dashboard.clerk.com/~/users) page. You can either select the user and then in the side navigation menu, select **Delete user**, or select the menu icon on the right side of the user's row and select **Delete user**. - app.post('/deleteUser', async (req, res) => { - const userId = req.body.userId +### Programmatically - try { - await clerkClient.users.deleteUser(userId) - res.status(200).json({ message: 'User deleted' }) - } catch (error) { - console.log(error) - res.status(500).json({ error: 'Error deleting user' }) - } - }) - ``` - +To delete a user programmatically, you can either [make a request directly to Clerk's Backend API](/docs/reference/backend/user/delete-user#backend-api-bapi-endpoint) or use the [`deleteUser()`](/docs/reference/backend/user/delete-user) method as shown in the following example. - - ```bash {{ filename: 'terminal' }} - curl 'https://api.clerk.com/v1/users/{user_id}' -X DELETE -H 'Authorization:Bearer {{secret}}' -H 'Content-Type:application/json' - ``` - - + diff --git a/docs/guides/users/reading.mdx b/docs/guides/users/reading.mdx index 3d9a95f323..0b98fa4388 100644 --- a/docs/guides/users/reading.mdx +++ b/docs/guides/users/reading.mdx @@ -1,7 +1,7 @@ --- title: Protect content and read user data description: Learn how to use Clerk's hooks and helpers to protect content and read user data in your Next.js application. -metadata: +metadata: title: Read session and user data in your Next.js app with Clerk sdk: nextjs --- @@ -36,7 +36,7 @@ The following example uses the [`auth()`](/docs/reference/nextjs/app-router/auth return
Sign in to view this page
} - // Get the Backend API User object when you need access to the user's information + // Get the Backend User object when you need access to the user's information const user = await currentUser() // Use `user` to render user details or create UI elements @@ -59,7 +59,7 @@ The following example uses the [`auth()`](/docs/reference/nextjs/app-router/auth return new NextResponse('Unauthorized', { status: 401 }) } - // Use `currentUser()` to get the Backend API User object + // Use `currentUser()` to get the Backend User object const user = await currentUser() // Add your Route Handler's logic with the returned `user` object @@ -76,9 +76,9 @@ For Next.js applications using the Pages Router, the [`getAuth()`](/docs/referen In some cases, you may need the full [`Backend User`](/docs/reference/backend/types/backend-user) object of the currently active user. This is helpful if you want to render information, like their first and last name, directly from the server. -The `clerkClient()` helper returns an instance of the [JS Backend SDK](/docs/js-backend/getting-started/quickstart), which exposes Clerk's Backend API resources through methods such as the [`getUser()`](/docs/reference/backend/user/get-user){{ target: '_blank' }} method. This method returns the full `Backend User` object. +The `clerkClient()` helper returns an instance of the [JS Backend SDK](/docs/js-backend/getting-started/quickstart), which exposes Clerk's Backend API resources through methods such as the [`getUser()`](/docs/reference/backend/user/get-user){{ target: '_blank' }} method. This method returns the full `Backend User` object. **It does count towards the [Backend API request rate limit](/docs/guides/how-clerk-works/system-limits)** so it's recommended to use the [`useUser()`](/docs/reference/hooks/use-user) hook on the client side when possible and only use `getUser()` when you specifically need user data in a server context. -In the following example, the `userId` is passed to the Backend SDK's `getUser()` method to get the user's full `Backend User` object. +In the following example, the `userId` is passed to the JS Backend SDK's `getUser()` method to get the user's full `Backend User` object. diff --git a/docs/guides/users/reading.remix.mdx b/docs/guides/users/reading.remix.mdx index 00f2adab93..f2dff9ab9a 100644 --- a/docs/guides/users/reading.remix.mdx +++ b/docs/guides/users/reading.remix.mdx @@ -14,9 +14,9 @@ Clerk provides a set of [hooks and helpers](/docs/reference/remix/overview) that ### `getAuth()` -The [`getAuth()`](/docs/reference/remix/overview#get-auth) helper allows you to access the [`Auth` object](/docs/reference/backend/types/auth-object){{ target: '_blank' }}, which includes the current user's `userId` and the `isAuthenticated` property, which can be used to protect your routes. +The [`getAuth()`](/docs/reference/remix/overview#get-auth) helper allows you to access the [`Auth` object](/docs/reference/backend/types/auth-object), which includes the current user's `userId` and the `isAuthenticated` property, which can be used to protect your routes. -In the following example, the `userId` is passed to the JS Backend SDK's [`getUser()`](/docs/reference/backend/user/get-user){{ target: '_blank' }} method to get the user's full `User` object. For information on how to use the JS Backend SDK, see the [JS Backend documentation](/docs/js-backend/getting-started/quickstart){{ target: '_blank' }}. +In the following example, the `userId` is passed to the JS Backend SDK's [`getUser()`](/docs/reference/backend/user/get-user) method to get the user's full `User` object. For information on how to use the JS Backend SDK, see the [reference documentation](/docs/js-backend/getting-started/quickstart). diff --git a/docs/guides/users/reading.tanstack-react-start.mdx b/docs/guides/users/reading.tanstack-react-start.mdx index d2b379d090..a4aacbee01 100644 --- a/docs/guides/users/reading.tanstack-react-start.mdx +++ b/docs/guides/users/reading.tanstack-react-start.mdx @@ -1,7 +1,7 @@ --- title: Protect content and read user data description: Learn how to use Clerk's hooks and helpers to protect content and read user data in your TanStack React Start application. -metadata: +metadata: title: Read session and user data in your Tanstack React Start app with Clerk sdk: tanstack-react-start --- @@ -21,14 +21,16 @@ In the following example, the `userId` is passed to the JS Backend SDK's `getUse ```tsx {{ filename: 'src/routes/index.tsx' }} import { createFileRoute, redirect } from '@tanstack/react-router' import { createServerFn } from '@tanstack/react-start' - import { auth, clerkClient } from '@clerk/tanstack-react-start/server' + import { clerkClient, auth } from '@clerk/tanstack-react-start/server' import { UserButton } from '@clerk/tanstack-react-start' const authStateFn = createServerFn({ method: 'GET' }).handler(async () => { - // Use `auth()` to access `isAuthenticated` and the user's ID + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object const { isAuthenticated, userId } = await auth() - // Protect the server function by checking if the user is signed in + // Protect the server function from unauthenticated users if (!isAuthenticated) { // This might error if you're redirecting to a path that doesn't exist yet // You can create a sign-in route to handle this @@ -70,22 +72,31 @@ In the following example, the `userId` is passed to the JS Backend SDK's `getUse ```ts {{ filename: 'src/routes/api/example.ts' }} import { auth, clerkClient } from '@clerk/tanstack-react-start/server' import { json } from '@tanstack/react-start' - import { createServerFileRoute } from '@tanstack/react-start/server' - - export const ServerRoute = createServerFileRoute('/api/example').methods({ - GET: async ({ request, params }) => { - // Use `auth()` to access `isAuthenticated` and the user's ID - const { isAuthenticated, userId } = await auth() - - // Protect the API route by checking if the user is signed in - if (!isAuthenticated) { - return json({ error: 'Unauthorized' }, { status: 401 }) - } - - // Get the user's full `Backend User` object - const user = await clerkClient().users.getUser(userId) - - return json({ user }) + import { createFileRoute } from '@tanstack/react-router' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + // The `Auth` object gives you access to properties like `isAuthenticated` and `userId` + // Accessing the `Auth` object differs depending on the SDK you're using + // https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object + const { isAuthenticated, userId } = await auth() + + // Protect the API route from unauthenticated users + if (!isAuthenticated) { + return new Response('User not authenticated', { + status: 404, + }) + } + + // Get the user's full `Backend User` object + const user = await clerkClient().users.getUser(userId) + + // Return the `Backend User` object + return json({ user }) + }, + }, }, }) ``` diff --git a/docs/reference/astro/clerk-middleware.mdx b/docs/reference/astro/clerk-middleware.mdx index b58376136f..ece546a5a1 100644 --- a/docs/reference/astro/clerk-middleware.mdx +++ b/docs/reference/astro/clerk-middleware.mdx @@ -45,7 +45,7 @@ import { clerkMiddleware, createRouteMatcher } from '@clerk/astro/server' const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/forum(.*)']) export const onRequest = clerkMiddleware((auth, context) => { - const { isAuthenticated, redirectToSignIn, userId } = auth() + const { isAuthenticated, redirectToSignIn } = auth() if (!isAuthenticated && isProtectedRoute(context.request)) { // Add custom logic to run before redirecting diff --git a/docs/reference/backend/authenticate-request.mdx b/docs/reference/backend/authenticate-request.mdx index df4cad9353..dcbe92ecf0 100644 --- a/docs/reference/backend/authenticate-request.mdx +++ b/docs/reference/backend/authenticate-request.mdx @@ -109,7 +109,7 @@ It is recommended to set these options as [environment variables](/docs/guides/d ## Examples - + If you are using the [JS Backend SDK](/docs/js-backend/getting-started/quickstart) on its own, you need to provide the `secretKey` and `publishableKey` to `createClerkClient()` so that it is passed to `authenticateRequest()`. You can set these values as [environment variables](/docs/guides/development/clerk-environment-variables#clerk-publishable-and-secret-keys) and then pass them to the function. @@ -138,23 +138,27 @@ It is recommended to set these options as [environment variables](/docs/guides/d - `authenticateRequest()` requires `publishableKey` to be set. If you are importing `clerkClient` from a higher-level SDK, such as Next.js, then `clerkClient` infers the `publishableKey` from your [environment variables](/docs/guides/development/clerk-environment-variables#clerk-publishable-and-secret-keys). The following example uses Next.js, but the same logic applies for other frameworks. + `authenticateRequest()` requires `publishableKey` to be set. If you are importing `clerkClient` from a higher-level SDK, such as Next.js, then `clerkClient` infers the `publishableKey` from your [environment variables](/docs/guides/development/clerk-environment-variables#clerk-publishable-and-secret-keys). **The following example uses Next.js, but you can use the comments in the example to help you adapt it to other SDKs.** ```tsx import { clerkClient } from '@clerk/nextjs/server' + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart const client = await clerkClient() export async function GET(req: Request) { + // Use the `authenticateRequest()` method to verify the token const { isAuthenticated } = await client.authenticateRequest(req, { authorizedParties: ['https://example.com'], }) + // Protect the route from unauthenticated users if (!isAuthenticated) { return Response.json({ status: 401 }) } - // Perform protected actions // Add logic to perform protected actions return Response.json({ message: 'This is a reply' }) @@ -167,31 +171,68 @@ It is recommended to set these options as [environment variables](/docs/guides/d By default, `authenticateRequest()` will authenticate a session request. To authenticate a machine request, you need to set the `acceptsToken` option to a machine token type, such as `'api_key'`, `'oauth_token'` or `'m2m_token'`. -```tsx -import { createClerkClient } from '@clerk/backend' + + + ```tsx + import { createClerkClient } from '@clerk/backend' + + export async function GET(request: Request) { + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + const clerkClient = createClerkClient({ + secretKey: process.env.CLERK_SECRET_KEY, + publishableKey: process.env.CLERK_PUBLISHABLE_KEY, + }) -export async function GET(request: Request) { - const clerkClient = createClerkClient({ - secretKey: process.env.CLERK_SECRET_KEY, - publishableKey: process.env.CLERK_PUBLISHABLE_KEY, - }) + // Use the `authenticateRequest()` method to verify the token + const { isAuthenticated } = await clerkClient.authenticateRequest(request, { + acceptsToken: 'oauth_token', + }) - const { isAuthenticated } = await clerkClient.authenticateRequest(request, { - acceptsToken: 'oauth_token', - }) + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return Response.json({ status: 401 }) + } - if (!isAuthenticated) { - return Response.json({ status: 401 }) - } + // Add logic to perform protected actions - // Add logic to perform protected actions - return Response.json({ message: 'This is a machine-to-machine reply' }) -} -``` + return Response.json({ message: 'This is a machine-to-machine reply' }) + } + ``` + -### Networkless token verification + + **This example uses Next.js, but you can use the comments in the example to help you adapt it to other SDKs.** -{/* Note: this example is duped from /authenticate-request. Probably a good opportunity to use a partial here */} + ```tsx + import { clerkClient } from '@clerk/nextjs/server' + + // Initialize the JS Backend SDK + // This varies depending on the SDK you're using + // https://clerk.com/docs/js-backend/getting-started/quickstart + const client = await clerkClient() + + export async function GET(req: Request) { + // Use the `authenticateRequest()` method to verify the token + const { isAuthenticated } = await client.authenticateRequest(request, { + acceptsToken: 'oauth_token', + }) + + // Protect the route from unauthenticated users + if (!isAuthenticated) { + return Response.json({ status: 401 }) + } + + // Add logic to perform protected actions + + return Response.json({ message: 'This is a machine-to-machine reply' }) + } + ``` + + + +### Networkless token verification The following example uses the `authenticateRequest()` method with the [JS Backend SDK](/docs/js-backend/getting-started/quickstart) to verify the token passed by the frontend, and performs a networkless authentication by passing `jwtKey`. This will verify if the user is signed into the application or not. diff --git a/docs/reference/backend/invitations/create-invitation.mdx b/docs/reference/backend/invitations/create-invitation.mdx index 8db251453e..9e84ee1d54 100644 --- a/docs/reference/backend/invitations/create-invitation.mdx +++ b/docs/reference/backend/invitations/create-invitation.mdx @@ -65,7 +65,7 @@ function createInvitation(params: CreateParams): Promise The slug of the email template to use for the invitation email. Defaults to `invitation`. -## Example +## Usage @@ -82,6 +82,24 @@ const response = await clerkClient.invitations.createInvitation({ }) ``` +## Example + + + ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `POST/invitations`. See the [BAPI reference](/docs/reference/backend-api/tag/invitations/post/invitations){{ target: '_blank' }} for more information. + +Here's an example of making a request directly to the endpoint using cURL. + + + Replace the email address with the email address you want to invite. Your Clerk Secret Key is already injected into the code snippet. + + + + Replace the email address with the email address you want to invite. Update `YOUR_SECRET_KEY` with your Clerk Secret Key which can be found on the [**API keys**](https://dashboard.clerk.com/~/api-keys) page in the Clerk Dashboard. + + +```bash {{ filename: 'terminal' }} +curl https://api.clerk.com/v1/invitations -X POST -d '{"email_address": "email@example.com"}' -H "Authorization:Bearer {{secret}}" -H 'Content-Type:application/json' +``` diff --git a/docs/reference/backend/invitations/revoke-invitation.mdx b/docs/reference/backend/invitations/revoke-invitation.mdx index 1b0089829b..43d4027bbd 100644 --- a/docs/reference/backend/invitations/revoke-invitation.mdx +++ b/docs/reference/backend/invitations/revoke-invitation.mdx @@ -25,7 +25,7 @@ function revokeInvitation(invitationId: string): Promise The ID of the invitation to revoke. -## Example +## Usage @@ -35,6 +35,24 @@ const invitationId = 'inv_123' const response = await clerkClient.invitations.revokeInvitation(invitationId) ``` +## Example + + + ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `POST/invitations/{invitation_id}/revoke`. See the [BAPI reference](/docs/reference/backend-api/tag/invitations/post/invitations/\{invitation_id}/revoke){{ target: '_blank' }} for more information. + +Here's an example of making a request directly to the endpoint using cURL. + + + Replace the `` with the ID of the invitation you want to revoke. Your Secret Key is already injected into the code snippet. + + + + Replace the `` with the ID of the invitation you want to revoke and replace `YOUR_SECRET_KEY` with your Clerk Secret Key. You can find your Secret Key on the [**API keys**](https://dashboard.clerk.com/~/api-keys) page in the Clerk Dashboard. + + +```bash {{ filename: 'terminal' }} +curl https://api.clerk.com/v1/invitations//revoke -X POST -H "Authorization:Bearer {{secret}}" -H 'Content-Type:application/json' +``` diff --git a/docs/reference/backend/user/create-user.mdx b/docs/reference/backend/user/create-user.mdx index 6e87f370c3..dd75b64160 100644 --- a/docs/reference/backend/user/create-user.mdx +++ b/docs/reference/backend/user/create-user.mdx @@ -225,7 +225,7 @@ function createUser(params: CreateUserParams): Promise A custom date/time denoting when the user signed up to the application, specified in [RFC3339 format](https://datatracker.ietf.org/doc/html/rfc3339). For example: `2012-10-20T07:15:20.902Z`. -## Example +## Usage @@ -238,6 +238,19 @@ const response = await clerkClient.users.createUser({ }) ``` +## Example + + + ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `POST/users`. See the [BAPI reference](/docs/reference/backend-api/tag/users/post/users){{ target: '_blank' }} for more information. + +Here's an example of making a request directly to the endpoint using cURL. + +```bash {{ filename: 'terminal' }} + curl 'https://api.clerk.com/v1/users' -X POST -H 'Authorization:Bearer {{secret}}' -H 'Content-Type:application/json' -d '{ + "email_address": ["test@example.com"], + "password": "my-secure-password" + }' +``` diff --git a/docs/reference/backend/user/delete-user.mdx b/docs/reference/backend/user/delete-user.mdx index c4fe74e82a..139f865b8c 100644 --- a/docs/reference/backend/user/delete-user.mdx +++ b/docs/reference/backend/user/delete-user.mdx @@ -21,7 +21,7 @@ function deleteUser(userId: string): Promise The ID of the user to delete. -## Example +## Usage @@ -31,6 +31,16 @@ const userId = 'user_123' const response = await clerkClient.users.deleteUser(userId) ``` +## Example + + + ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `DELETE/users/{user_id}`. See the [BAPI reference](/docs/reference/backend-api/tag/users/delete/users/\{user_id}){{ target: '_blank' }} for more information. + +Here's an example of making a request directly to the endpoint using cURL. + +```bash {{ filename: 'terminal' }} + curl 'https://api.clerk.com/v1/users/{user_id}' -X DELETE -H 'Authorization:Bearer {{secret}}' -H 'Content-Type:application/json' +``` diff --git a/docs/reference/backend/user/update-user-metadata.mdx b/docs/reference/backend/user/update-user-metadata.mdx index 7c0a244b48..9cd2e2755e 100644 --- a/docs/reference/backend/user/update-user-metadata.mdx +++ b/docs/reference/backend/user/update-user-metadata.mdx @@ -39,7 +39,7 @@ function updateUserMetadata(userId: string, params: UpdateUserMetadataParams): P Metadata that can be read and set only from the [Backend API](/docs/reference/backend-api){{ target: '_blank' }}. -## Example +## Usage @@ -53,6 +53,123 @@ const response = await clerkClient.users.updateUserMetadata(userId, { }) ``` +## Example + +This example updates the user's public metadata to include a birthday, but you can update the public, private, or unsafe metadata to include any information you want. + + + + ```ts {{ filename: 'app/api/example/route.ts' }} + import { auth, clerkClient } from '@clerk/nextjs/server' + import { NextResponse } from 'next/server' + + export async function GET() { + const { userId } = await auth() + const client = await clerkClient() + + await client.users.updateUserMetadata(userId, { + publicMetadata: { + birthday: '1990-01-01', + }, + }) + + return NextResponse.json({ success: true }) + } + ``` + + + + ```tsx {{ filename: 'src/api/example.ts' }} + import type { APIRoute } from 'astro' + import { clerkClient } from '@clerk/astro/server' + + export const GET: APIRoute = async (context) => { + const { userId } = context.locals.auth() + + await clerkClient(context).users.updateUserMetadata(userId, { + publicMetadata: { + birthday: '1990-01-01', + }, + }) + + return new Response(JSON.stringify({ success: true }), { status: 200 }) + } + ``` + + + + ```ts {{ filename: 'public.ts' }} + import { getAuth, clerkClient } from '@clerk/express' + + app.post('/updateBirthday', async (req, res) => { + const { userId } = getAuth(req) + + await clerkClient.users.updateUserMetadata(userId, { + publicMetadata: { + birthday: '1990-01-01', + }, + }) + res.status(200).json({ success: true }) + }) + ``` + + + + ```tsx {{ filename: 'app/routes/example.tsx' }} + import { clerkClient, getAuth } from '@clerk/react-router/server' + import type { Route } from './+types/example' + + export async function loader(args: Route.LoaderArgs) { + const { userId } = await getAuth(args) + + await clerkClient.users.updateUserMetadata(userId, { + publicMetadata: { + birthday: '1990-01-01', + }, + }) + + return { success: true } + } + ``` + + + + ```tsx {{ filename: 'app/routes/api/example.tsx' }} + import { json } from '@tanstack/react-start' + import { createFileRoute } from '@tanstack/react-router' + import { auth, clerkClient } from '@clerk/tanstack-react-start/server' + + export const ServerRoute = createFileRoute('/api/example')({ + server: { + handlers: { + GET: async () => { + const { userId } = await auth() + + await clerkClient().users.updateUserMetadata(userId, { + publicMetadata: { + birthday: '1990-01-01', + }, + }) + + return json({ success: true }) + }, + }, + }, + }) + ``` + + + ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `PATCH/users/{user_id}/metadata`. See the [BAPI reference](/docs/reference/backend-api/tag/users/patch/users/\{user_id}/metadata){{ target: '_blank' }} for more information. + +Here's an example of making a request directly to the endpoint using cURL. + +```bash {{ filename: 'curl.sh' }} +curl -XPATCH -H 'Authorization: Bearer CLERK_SECRET_KEY' -H "Content-type: application/json" -d '{ + "public_metadata": { + "birthday": "1990-01-01" + } +}' 'https://api.clerk.com/v1/users/{user_id}/metadata' +``` diff --git a/docs/reference/nextjs/overview.mdx b/docs/reference/nextjs/overview.mdx index 74fd2fd8e7..bee4876ca3 100644 --- a/docs/reference/nextjs/overview.mdx +++ b/docs/reference/nextjs/overview.mdx @@ -34,6 +34,12 @@ Clerk continues to provide drop-in support for the Next.js Pages Router. In addi - [`getAuth()`](/docs/reference/nextjs/pages-router/get-auth) - [`buildClerkProps()`](/docs/reference/nextjs/pages-router/build-clerk-props) +## `clerkClient` + +[Clerk's JS Backend SDK](/docs/js-backend/getting-started/quickstart) provides access to Backend API resources and low-level authentication utilities for JavaScript environments. For example, to retrieve a list of all users in your application, you can use the `users.getUserList()` method from the JS Backend SDK instead of manually making a fetch request to the [https://api.clerk.com/v1/users](https://clerk.com/docs/reference/backend-api/tag/users/get/users) endpoint. + +All resource operations are mounted as sub-APIs on the `clerkClient` object. See the [reference documentation](/docs/js-backend/getting-started/quickstart#usage) for more information. + ## `Auth` object Both `auth()` (App Router) and `getAuth()` (Pages Router) return an `Auth` object. This JavaScript object contains important information like the current user's session ID, user ID, and organization ID. Learn more about the [`Auth` object](/docs/reference/backend/types/auth-object){{ target: '_blank' }}.