Repository: workos/authkit Branch: main Commit: abb80ebad1b6 Files: 55 Total size: 73.6 KB Directory structure: gitextract_xtqy99mp/ ├── .eslintrc.json ├── .github/ │ └── workflows/ │ ├── coana-analysis.yml │ └── coana-guardrail.yml ├── .gitignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── context7.json ├── next.config.js ├── package.json ├── src/ │ ├── app/ │ │ ├── back-link.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── page.tsx │ │ ├── using-hosted-authkit/ │ │ │ ├── README.md │ │ │ ├── basic/ │ │ │ │ ├── callback/ │ │ │ │ │ └── route.ts │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ ├── with-nextjs/ │ │ │ │ ├── callback/ │ │ │ │ │ └── route.ts │ │ │ │ └── page.tsx │ │ │ └── with-session/ │ │ │ ├── auth.ts │ │ │ ├── callback/ │ │ │ │ └── route.ts │ │ │ └── page.tsx │ │ └── using-your-own-ui/ │ │ ├── README.md │ │ ├── mfa/ │ │ │ ├── mfa.ts │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── reset-password/ │ │ │ ├── page.tsx │ │ │ └── reset-password.ts │ │ ├── sign-in/ │ │ │ ├── email-password/ │ │ │ │ ├── email-password.ts │ │ │ │ └── page.tsx │ │ │ ├── github-oauth/ │ │ │ │ ├── callback/ │ │ │ │ │ └── route.ts │ │ │ │ └── page.tsx │ │ │ ├── google-oauth/ │ │ │ │ ├── callback/ │ │ │ │ │ └── route.ts │ │ │ │ └── page.tsx │ │ │ ├── magic-auth/ │ │ │ │ ├── magic-auth.ts │ │ │ │ └── page.tsx │ │ │ ├── microsoft-oauth/ │ │ │ │ ├── callback/ │ │ │ │ │ └── route.ts │ │ │ │ └── page.tsx │ │ │ └── sso/ │ │ │ ├── callback/ │ │ │ │ └── route.ts │ │ │ └── page.tsx │ │ ├── sign-up/ │ │ │ ├── email-password/ │ │ │ │ ├── email-password.ts │ │ │ │ └── page.tsx │ │ │ └── magic-auth/ │ │ │ ├── magic-auth.ts │ │ │ └── page.tsx │ │ ├── update-user/ │ │ │ ├── page.tsx │ │ │ └── update-user.ts │ │ ├── users-table/ │ │ │ ├── loading.tsx │ │ │ ├── page.tsx │ │ │ └── users-table.ts │ │ └── verify-email/ │ │ ├── page.tsx │ │ └── verify-email.ts │ └── middleware.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "extends": "next/core-web-vitals" } ================================================ FILE: .github/workflows/coana-analysis.yml ================================================ name: Coana Vulnerability Analysis on: schedule: - cron: "0 3 * * *" # every day at 3 AM workflow_dispatch: inputs: tags: description: "Manually run vulnerability analysis" # Required by the return-dispatch action distinct_id: jobs: coana-vulnerability-analysis: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Coana CLI id: coana-cli uses: docker://coana/coana:latest@sha256:74144ed0fc9d7da87dcd45ccd12458cc7c25ad23e47eebd7ceb4860ed396d63e with: args: | coana run . \ --api-key ${{ secrets.COANA_API_KEY }} \ --repo-url https://github.com/${{github.repository}} ================================================ FILE: .github/workflows/coana-guardrail.yml ================================================ name: Coana Guardrail on: pull_request jobs: guardrail: runs-on: ubuntu-latest steps: - name: Checkout the ${{github.base_ref}} branch uses: actions/checkout@v4 with: ref: ${{github.base_ref}} # checkout the base branch (usually master/main). - name: Fetch the PR branch run: | git fetch ${{ github.event.pull_request.head.repo.clone_url }} ${{ github.head_ref }}:${{ github.head_ref }} --depth=1 - name: Get list of changed files relative to the main/master branch id: changed-files run: | echo "all_changed_files=$(git diff --name-only ${{ github.base_ref }} ${{ github.head_ref }} | tr '\n' ' ')" >> $GITHUB_OUTPUT - name: Use Node.js 20.x uses: actions/setup-node@v4 with: node-version: 20.x - name: Run Coana on the ${{github.base_ref}} branch run: | npx @coana-tech/cli run . \ --guardrail-mode \ --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \ -o /tmp/main-branch \ --changed-files ${{ steps.changed-files.outputs.all_changed_files }} \ --lightweight-reachability \ # Reset file permissions. # This is necessary because the Coana CLI may add # new files with root ownership since it's using docker. # These files will not be deleted by the clean step in checkout # if the permissions are not reset. - name: Reset file permissions run: sudo chown -R $USER:$USER . - name: Checkout the current branch uses: actions/checkout@v4 with: clean: true - name: Run Coana on the current branch run: | npx @coana-tech/cli run . \ --guardrail-mode \ --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \ -o /tmp/current-branch \ --changed-files ${{ steps.changed-files.outputs.all_changed_files }} \ --lightweight-reachability \ - name: Run Report Comparison run: | npx @coana-tech/cli compare-reports \ --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \ /tmp/main-branch/coana-report.json \ /tmp/current-branch/coana-report.json env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js .yarn/install-state.gz # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env*.local # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts ================================================ FILE: .prettierrc ================================================ { "printWidth": 100, "singleQuote": true } ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at support@workos.com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 WorkOS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

AuthKit

How to use AuthKit's hosted UI or build your own frontend with the headless User Management APIs

Explore the docs ↗

Screenshot of hosted UI AuthKit in light mode

## Examples There are two ways to use AuthKit and this repository contains examples for both: - [Using AuthKit's hosted UI](./src/app/using-hosted-authkit) This is the fastest way to add authentication to your app with AuthKit and WorkOS User Management. It includes a fully themeable hosted UI that handles all of your authentication flows. When you're ready to go to production you can point it to a custom domain (`auth.yourapp.com`) to match your application. - [Using your own custom UI](./src/app/using-your-own-ui) Use all of the features of AuthKit, but build out the UI yourself in your own codebase by integrating directly with the headless WorkOS User Management APIs. Your authentication UI will be self-hosted in your application. ## Prerequisites You will need a [WorkOS account](https://dashboard.workos.com/signup). ## Running the example 1. Install dependencies with `npm install` or `yarn install` 2. Set up your **Environment variables** by signing into your [WorkOS dashboard](https://dashboard.workos.com), navigate to **API Keys** and copy the **Client ID** and the **Secret Key** (API Key). Rename the `.env.local.example` file to `.env.local` and supply your _Client ID_ and _Secret Key_. ```bash WORKOS_CLIENT_ID="" WORKOS_API_KEY="" ``` 3. Configure redirects in your [WorkOS dashboard](https://dashboard.workos.com), navigate to **Redirects** and add the following urls: ```bash http://localhost:3000/using-your-own-ui/sign-in/google-oauth/callback ``` ```bash http://localhost:3000/using-your-own-ui/sign-in/microsoft-oauth/callback ``` ```bash http://localhost:3000/using-your-own-ui/sign-in/github-oauth/callback ``` ```bash http://localhost:3000/using-your-own-ui/sign-in/sso/callback ``` ```bash http://localhost:3000/using-hosted-authkit/basic/callback ``` ```bash http://localhost:3000/using-hosted-authkit/with-session/callback ``` ```bash http://localhost:3000/using-hosted-authkit/with-nextjs/callback ``` 4. Run the example with `npm run dev` or `yarn dev` and navigate to http://localhost:3000 ================================================ FILE: context7.json ================================================ { "url": "https://context7.com/workos/authkit", "public_key": "pk_q7NnKuFFXMWA7WnmjMHQU" } ================================================ FILE: next.config.js ================================================ /** @type {import('next').NextConfig} */ const nextConfig = {} module.exports = nextConfig ================================================ FILE: package.json ================================================ { "name": "authkit", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@workos-inc/authkit-nextjs": "0.4.2", "@workos-inc/node": "^6.7.0", "jose": "^5.2.3", "next": "14.1.4", "react": "^18", "react-dom": "^18" }, "devDependencies": { "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "14.1.4", "typescript": "^5" } } ================================================ FILE: src/app/back-link.tsx ================================================ 'use client'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; export default function BackLink() { const pathname = usePathname(); const segments = pathname.split('/').filter(Boolean); if (segments.length === 0) return null; if (segments.length === 1) return Home; return Examples; } ================================================ FILE: src/app/globals.css ================================================ ::selection { background-color: #003eff3e; } body { margin: 0; font-size: 20px; } a { color: black; text-decoration-thickness: 1px; text-underline-offset: 2px; text-decoration-color: #888; } /* back link */ body > a { position: fixed; top: 12px; left: 12px; } main { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; box-sizing: border-box; padding-top: 48px; } h1 { text-align: center; margin: 0; margin-top: auto; margin-bottom: 24px; } h1:has(+ h2) { margin-bottom: 8px; } h2 { text-align: center; margin: 0; margin-bottom: 32px; } h2:has(+ ul) { margin-top: 24px; margin-bottom: 8px; } main > form { display: flex; flex-direction: column; width: 400px; gap: 24px; margin-bottom: auto; } /* field (label + input) */ form > div { display: flex; flex-direction: column; gap: 8px; } label { font-size: 16px; font-weight: bold; } input { width: 100%; box-sizing: border-box; border: 2px solid black; border-radius: 4px; font-size: 20px; font-family: inherit; height: 48px; padding: 0 12px; } button, :where(main, form) > a { display: flex; align-items: center; justify-content: center; width: 100%; box-sizing: border-box; background-color: black; color: white; border-radius: 4px; font-size: 20px; height: 48px; font-family: inherit; border: 0; padding: 0 12px; margin: 8px 0; cursor: pointer; text-decoration: none; } button:active, :where(main, form) > a:active { background-color: #333; } main > a { width: 400px; margin-bottom: auto; } pre { font-size: 16px; box-sizing: border-box; min-height: 250px; height: auto; overflow: auto; width: 100vw; margin: 0; margin-top: 40px; bottom: 0; left: 0; right: 0; padding: 20px; background-color: #fafafa; border-top: 1px solid #eee; } /* lists for index pages */ ul { text-align: center; list-style-type: none; margin: 0; padding-left: 0; } h2 + ul:not(:last-child) { margin-bottom: 36px; } h1 + ul:last-child, h2 + ul:last-child { margin-bottom: auto; padding-bottom: 48px; } li { margin: 8px 0; } /* table for users table page */ main:has(table) h1 { margin-top: 0; } table { width: 100%; border-collapse: collapse; } th, td { text-align: left; padding: 12px 24px; border-bottom: 1px solid black; } table + nav { box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; padding: 24px; width: 100%; gap: 24px; margin-bottom: auto; color: #888; } table button { margin: 0; } ================================================ FILE: src/app/layout.tsx ================================================ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import './globals.css'; import BackLink from './back-link'; const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { title: 'AuthKit', description: 'A collection of examples for AuthKit', }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ================================================ FILE: src/app/page.tsx ================================================ import Link from 'next/link'; export default function Home() { return (

Examples

  • Using your own UI
  • Using hosted AuthKit
); } ================================================ FILE: src/app/using-hosted-authkit/README.md ================================================

Using AuthKit's hosted UI

How to use AuthKit with themeable hosted UI

View the examples  ·  Explore the docs ↗




## Setup First, ensure you have set up the [environment variables](/#environment-variables) and [redirects](/#redirects). For each example, you will need to ensure the applicable authentication method is enabled in your WorkOS dashboard. To do so navigate to **Authentication** and edit the applicable authentication method and ensure it is set to **Enabled**. For the Google OAuth and Microsoft OAuth examples, WorkOS provides demo app credentials to use in your WorkOS staging environment. This allows you to test these authentication flows without having to set up your own OAuth apps. In order to test Single Sign-On, you will need to create an organization in your WorkOS dashboard. Navigate to **Organizations** and then **Create organization**. Enter a name for this organization, and optionally add a domain that the members will use to sign in. You will also need to create a Single Sign-On connection in the WorkOS dashboard for this organization. On this organization's detail page, navigate to the authentication section, find **Single Sign-On**. For the purposes of this example, we will use the **Configure Manually** feature to create a new connection. This requires you to have access to an identity provider (IdP) for testing such as Entra ID (Azure AD), Google Workspace, or Okta. ## Examples - [Basic authentication](./basic/page.tsx). How to use AuthKit's hosted UI with any authentication method (Email + Password, Magic Auth, Google OAuth, Microsoft OAuth, and Single Sign-On). - [Using the authkit-nextjs library](./with-nextjs/page.tsx). How to use AuthKit's hosted UI in Next.js with managed client-side sessions and impersonation. - [With client-side sessions](./with-session/page.tsx). How to use AuthKit's hosted UI and manage sessions client-side using JavaScript Web Tokens (JWTs). ### Next.js For the `authkit-nextjs` example, you'll need to add the following to your environment variables: ```bash # Needed for authkit-nextjs library example, defined in WorkOS dashboard WORKOS_REDIRECT_URI= # Needed for authkit-nextjs library example. Must be at least 32 characters long WORKOS_COOKIE_PASSWORD= ``` To generate a secure cookie password, you can use the [1Password generator](https://1password.com/password-generator/) or use the `openssl` library to generate a strong password on the command line: ```bash openssl rand -base64 24 ``` ### Sessions For the example with client-side sessions, you will need to add a JWT secret as an environment variable. It can be any random base64 string for testing locally. You can use the `openssl` library to easily generate a key. ```bash openssl rand -base64 32 ``` And update the `.env.local` file: ```bash # ... JWT_SECRET_KEY="" ``` ================================================ FILE: src/app/using-hosted-authkit/basic/callback/route.ts ================================================ import { WorkOS } from '@workos-inc/node'; import { redirect } from 'next/navigation'; // This is a Next.js Route Handler. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle the request // - adapt the code below in your endpoint // // Please also note that for the sake of simplicity, we directly return the user here in the query string. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies (See `with-session` example for more details). const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function GET(request: Request) { const code = new URL(request.url).searchParams.get('code') || ''; let response; try { response = await workos.userManagement.authenticateWithCode({ clientId: process.env.WORKOS_CLIENT_ID || '', code, }); } catch (error) { response = error; } if (response) { redirect( `http://localhost:3000/using-hosted-authkit/basic?response=${JSON.stringify(response)}` ); } } ================================================ FILE: src/app/using-hosted-authkit/basic/page.tsx ================================================ import { WorkOS } from '@workos-inc/node'; // This example uses Next.js with React Server Components. // Because this page is an RSC, the code stays on the server, which allows // us to use the WorkOS Node SDK without exposing our API key to the client. // // If your application is a single page app (SPA), you will need to: // - create a form that can POST to an endpoint in your backend // - call the `getAuthorizationURL` method in that endpoint // - redirect the user to the returned URL const workos = new WorkOS(process.env.WORKOS_API_KEY); export default function Basic({ searchParams, }: { searchParams: { [key: string]: string | string[] | undefined }; }) { const authKitUrl = workos.userManagement.getAuthorizationUrl({ clientId: process.env.WORKOS_CLIENT_ID || '', provider: 'authkit', redirectUri: 'http://localhost:3000/using-hosted-authkit/basic/callback', }); const result = JSON.parse(String(searchParams.response ?? '{ "error": null }')); return (

Using hosted AuthKit

Basic example

Sign-in with AuthKit
{JSON.stringify(result, null, 2)}
); } ================================================ FILE: src/app/using-hosted-authkit/page.tsx ================================================ import Link from 'next/link'; export default function UsingHostedAuthKit() { return (

Using hosted AuthKit

  • Basic example
  • With Next.js library
  • With session
); } ================================================ FILE: src/app/using-hosted-authkit/with-nextjs/callback/route.ts ================================================ import { handleAuth } from '@workos-inc/authkit-nextjs'; export const GET = handleAuth({ returnPathname: '/using-hosted-authkit/with-nextjs/' }); ================================================ FILE: src/app/using-hosted-authkit/with-nextjs/page.tsx ================================================ import { getSignInUrl, getUser, signOut } from '@workos-inc/authkit-nextjs'; export default async function WithNextjs() { // Retrieves the user from the session or returns `null` if no user is signed in const { user } = await getUser(); // Get the URL to redirect the user to AuthKit to sign in const signInUrl = await getSignInUrl(); return (

Using hosted AuthKit

With Next.js library

{user ? ( <>

Welcome back {user?.firstName && `, ${user?.firstName}`}

{ 'use server'; await signOut(); }} >
) : ( Sign in )}
{JSON.stringify(user, null, 2)}
); } ================================================ FILE: src/app/using-hosted-authkit/with-session/auth.ts ================================================ import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; import { jwtVerify } from 'jose'; import type { User } from '@workos-inc/node'; export function getJwtSecretKey() { const secret = process.env.JWT_SECRET_KEY; if (!secret) { throw new Error('JWT_SECRET_KEY is not set'); } return new Uint8Array(Buffer.from(secret, 'base64')); } export async function verifyJwtToken(token: string) { try { const { payload } = await jwtVerify(token, getJwtSecretKey()); return payload; } catch (error) { return null; } } // Verify the JWT and return the user export async function getUser(): Promise<{ isAuthenticated: boolean; user?: User | null; }> { const token = cookies().get('token')?.value; if (token) { const verifiedToken = await verifyJwtToken(token); if (verifiedToken) { return { isAuthenticated: true, user: verifiedToken.user as User | null, }; } } return { isAuthenticated: false }; } // Clear the session and redirect to the home page export async function signOut() { cookies().delete('token'); redirect('/using-hosted-authkit/with-session'); } ================================================ FILE: src/app/using-hosted-authkit/with-session/callback/route.ts ================================================ import { WorkOS } from '@workos-inc/node'; import { NextResponse } from 'next/server'; import { SignJWT } from 'jose'; import { getJwtSecretKey } from '../auth'; // This is a Next.js Route Handler. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle the request // - adapt the code below in your endpoint const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function GET(request: Request) { const url = new URL(request.url); const code = url.searchParams.get('code') || ''; try { const { user } = await workos.userManagement.authenticateWithCode({ clientId: process.env.WORKOS_CLIENT_ID || '', code, }); // Create a JWT with the user's information // Here you might lookup and retrieve user details from your database const token = await new SignJWT({ user }) .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) .setIssuedAt() .setExpirationTime('1h') .sign(getJwtSecretKey()); // Cleanup params url.searchParams.delete('code'); // Store the session and redirect to the application url.pathname = '/using-hosted-authkit/with-session'; const response = NextResponse.redirect(url); response.cookies.set({ name: 'token', value: token, httpOnly: true, path: '/', secure: true, sameSite: 'lax', }); return response; } catch (error) { return NextResponse.json(error); } } ================================================ FILE: src/app/using-hosted-authkit/with-session/page.tsx ================================================ import { WorkOS } from '@workos-inc/node'; import { getUser, signOut } from './auth'; // This example uses Next.js with React Server Components. // Because this page is an RSC, the code stays on the server, which allows // us to use the WorkOS Node SDK without exposing our API key to the client. // // If your application is a single page app (SPA), you will need to: // - create a form that can POST to an endpoint in your backend // - call the `getAuthorizationURL` method in that endpoint // - redirect the user to the returned URL const workos = new WorkOS(process.env.WORKOS_API_KEY); export default async function WithSession() { const { isAuthenticated, user } = await getUser(); const authKitUrl = workos.userManagement.getAuthorizationUrl({ clientId: process.env.WORKOS_CLIENT_ID || '', provider: 'authkit', redirectUri: 'http://localhost:3000/using-hosted-authkit/with-session/callback', }); return (

With session

{isAuthenticated ? ( <>

Welcome back{user?.firstName && `, ${user?.firstName}`}

You are now authenticated into the application.

{ 'use server'; await signOut(); }} >
) : ( <>

Sign-in

Sign-in to view your account details

Sign-in )}
{JSON.stringify({ user }, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/README.md ================================================

Using AuthKit with your own UI

How to use AuthKit with your own frontend using headless User Management APIs

View the examples  ·  Explore the docs ↗




## Setup First, ensure you have set up the [environment variables](/#environment-variables) and [redirects](/#redirects). For each example, you will need to ensure the applicable authentication method is enabled in your WorkOS dashboard. To do so navigate to **Authentication** and edit the applicable authentication method and ensure it is set to **Enabled**. For the Google OAuth and Microsoft OAuth examples, WorkOS provides demo app credentials to use in your WorkOS staging environment. This allows you to test these authentication flows without having to set up your own OAuth apps. ## Examples ### Sign-up - [Email + Password](./sign-up/email-password/page.tsx) - [Magic Auth](./sign-up/magic-auth/page.tsx) ### Sign-in - [Email + Password](./sign-in/email-password/page.tsx) - [Magic Auth](./sign-in/magic-auth/page.tsx) - [Google OAuth](./sign-in/google-oauth/page.tsx) - [Microsoft OAuth](./sign-in/microsoft-oauth/page.tsx) - [Single Sign-On](./sign-in/sso/page.tsx) For the Single Sign-On example, you will need to create an organization in your WorkOS dashboard. Navigate to **Organizations** and then **Create organization**. Enter a name for this organization, and optionally add a domain that the members will use to sign in. You will also need to create a Single Sign-On connection in the WorkOS dashboard for this organization. On this organization's detail page, navigate to the authentication section, find **Single Sign-On**. For the purposes of this example, we will use the **Configure Manually** feature to create a new connection. This requires you to have access to an identity provider (IdP) for testing such as Entra ID (Azure AD), Google Workspace, or Okta. You will also need to copy the **Organization ID** from the organization you created with the active Single Sign-On connection. This is located below the organization's name on its detail page (beginning with `org_`). Copy it and add it to your `.env.local` file. ```bash SSO_ENABLED_ORGANIZATION_ID="" ``` ### Other - [Multi-Factor Auth](./mfa/page.tsx) - [Verify email](./verify-email/page.tsx) - [Reset password](./reset-password/page.tsx) - [Users table](./users-table/page.tsx) - [Update user](./update-user/page.tsx) ================================================ FILE: src/app/using-your-own-ui/mfa/mfa.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function signIn(prevState: any, formData: FormData): Promise { try { // For the sake of simplicity, we directly return the user here. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies. return await workos.userManagement.authenticateWithPassword({ clientId: process.env.WORKOS_CLIENT_ID || '', email: String(formData.get('email')), password: String(formData.get('password')), }); } catch (error) { const err = JSON.parse(JSON.stringify(error)); if (err.rawData.code === 'mfa_enrollment') { const { authenticationFactor, authenticationChallenge } = await workos.userManagement.enrollAuthFactor({ userId: err.rawData.user.id, type: 'totp', totpIssuer: 'WorkOS', totpUser: err.rawData.user.email, }); return { authenticationFactor, authenticationChallenge, pendingAuthenticationToken: err.rawData.pending_authentication_token, }; } if (err.rawData.code === 'mfa_challenge') { const challenge = await workos.mfa.challengeFactor({ authenticationFactorId: err.rawData.authentication_factors[0].id, }); return { authenticationChallenge: challenge, pendingAuthenticationToken: err.rawData.pending_authentication_token, }; } return { error: err }; } } export async function verifyTotp(prevState: any, formData: FormData) { try { // For the sake of simplicity, we directly return the user here. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies. return await workos.userManagement.authenticateWithTotp({ clientId: process.env.WORKOS_CLIENT_ID || '', authenticationChallengeId: String(formData.get('authenticationChallengeId')), pendingAuthenticationToken: String(formData.get('pendingAuthenticationToken')), code: String(formData.get('code')), }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } type UnpackPromise = T extends Promise ? U : T; type AuthenticateResponse = UnpackPromise< ReturnType >; type EnrollResponse = UnpackPromise>; type SignInResponse = | AuthenticateResponse | { authenticationFactor?: EnrollResponse['authenticationFactor']; authenticationChallenge: EnrollResponse['authenticationChallenge']; pendingAuthenticationToken: string; } | { error: any }; ================================================ FILE: src/app/using-your-own-ui/mfa/page.tsx ================================================ 'use client'; import { useFormState } from 'react-dom'; import { signIn, verifyTotp } from './mfa'; import Image from 'next/image'; export default function Mfa() { // This example uses Next.js server actions to call functions on the server side. // // If your application is a single page app (SPA), you will need to: // - handle the form submission in `
` // - make an API call to your backend (e.g using `fetch`) const [signInState, signInAction] = useFormState(signIn, { error: null }); const [verifyState, verifyAction] = useFormState(verifyTotp, { error: null }); if (!('authenticationChallenge' in signInState) || 'user' in signInState) { return (

Multi-Factor Auth

Sign-in

{JSON.stringify(signInState, null, 2)}
); } return (

Multi-Factor Auth

{signInState.authenticationFactor ? ( <>

Enroll

Scan the QR code

QR code

then

) : (

Verify

)}
{JSON.stringify(verifyState, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/page.tsx ================================================ import Link from 'next/link'; export default function UsingYourOwnUi() { return (

Using your own UI

Sign-up

  • Email + Password
  • Magic Auth

Sign-in

  • Email + Password
  • Magic Auth
  • GitHub OAuth
  • Google OAuth
  • Microsoft OAuth
  • Single Sign-On

Other

  • Multi-Factor Auth
  • Verify email
  • Reset password
  • Users table
  • Update user
); } ================================================ FILE: src/app/using-your-own-ui/reset-password/page.tsx ================================================ 'use client'; import { useFormState } from 'react-dom'; import { sendReset, resetPassword } from './reset-password'; export default function ResetPassword({ searchParams, }: { searchParams: { token?: string; email?: string }; }) { const { token, email } = searchParams; // This example uses Next.js server actions to call functions on the server side. // // If your application is a single page app (SPA), you will need to: // - handle the form submission in `
` // - make an API call to your backend (e.g using `fetch`) const [sendResetState, sendResetAction] = useFormState(sendReset, { error: null }); const [resetPasswordState, resetPasswordAction] = useFormState(resetPassword, { error: null }); if (!token) { return (

Reset password

{JSON.stringify(sendResetState, null, 2)}
); } return (

Reset password

{email && ( // We also include the email in a hidden input so that password managers can update the password on the correct account. // https://developer.1password.com/docs/web/compatible-website-design/#password-change-and-reset-forms )}
{JSON.stringify(resetPasswordState, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/reset-password/reset-password.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function sendReset(prevState: any, formData: FormData) { try { const email = String(formData.get('email')); return await workos.userManagement.sendPasswordResetEmail({ email, passwordResetUrl: `http://localhost:3000/using-your-own-ui/reset-password?email=${email}`, }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } export async function resetPassword(prevState: any, formData: FormData) { try { return await workos.userManagement.resetPassword({ newPassword: String(formData.get('newPassword')), token: String(formData.get('token')), }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } ================================================ FILE: src/app/using-your-own-ui/sign-in/email-password/email-password.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function signIn(prevState: any, formData: FormData) { try { // For the sake of simplicity, we directly return the user here. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies. return await workos.userManagement.authenticateWithPassword({ clientId: process.env.WORKOS_CLIENT_ID || '', email: String(formData.get('email')), password: String(formData.get('password')), }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } ================================================ FILE: src/app/using-your-own-ui/sign-in/email-password/page.tsx ================================================ 'use client'; import { useFormState } from 'react-dom'; import { signIn } from './email-password'; export default function SignInWithEmailPassword() { // This example uses Next.js server actions to call functions on the server side. // // If your application is a single page app (SPA), you will need to: // - handle the form submission in `
` // - make an API call to your backend (e.g using `fetch`) const [signInState, signInAction] = useFormState(signIn, { error: null }); return (

Sign-in

Email + Password

{JSON.stringify(signInState, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/sign-in/github-oauth/callback/route.ts ================================================ import { WorkOS } from '@workos-inc/node'; import { redirect } from 'next/navigation'; // This is a Next.js Route Handler. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle the request // - adapt the code below in your endpoint // // Please also note that for the sake of simplicity, we directly return the user here in the query string. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies. const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function GET(request: Request) { const code = new URL(request.url).searchParams.get('code') || ''; let response; try { response = await workos.userManagement.authenticateWithCode({ clientId: process.env.WORKOS_CLIENT_ID || '', code, }); } catch (error) { response = error; } if (response) { redirect( `http://localhost:3000/using-your-own-ui/sign-in/github-oauth?response=${JSON.stringify( response )}` ); } } ================================================ FILE: src/app/using-your-own-ui/sign-in/github-oauth/page.tsx ================================================ import { WorkOS } from '@workos-inc/node'; // This example uses Next.js with React Server Components. // Because this page is an RSC, the code stays on the server, which allows // us to use the WorkOS Node SDK without exposing our API key to the client. // // If your application is a single page app (SPA), you will need to: // - create a form that can POST to an endpoint in your backend // - call the `getAuthorizationURL` method in that endpoint // - redirect the user to the returned URL const workos = new WorkOS(process.env.WORKOS_API_KEY); export default function SignInWithGitHubOAuth({ searchParams, }: { searchParams: { [key: string]: string | string[] | undefined }; }) { const githubOAuthUrl = workos.userManagement.getAuthorizationUrl({ clientId: process.env.WORKOS_CLIENT_ID || '', provider: 'GitHubOAuth', redirectUri: 'http://localhost:3000/using-your-own-ui/sign-in/github-oauth/callback', }); const result = JSON.parse(String(searchParams.response ?? '{ "error": null }')); return (

Sign-in

GitHub OAuth

Continue with GitHub
{JSON.stringify(result, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/sign-in/google-oauth/callback/route.ts ================================================ import { WorkOS } from '@workos-inc/node'; import { redirect } from 'next/navigation'; // This is a Next.js Route Handler. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle the request // - adapt the code below in your endpoint // // Please also note that for the sake of simplicity, we directly return the user here in the query string. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies. const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function GET(request: Request) { const code = new URL(request.url).searchParams.get('code') || ''; let response; try { response = await workos.userManagement.authenticateWithCode({ clientId: process.env.WORKOS_CLIENT_ID || '', code, }); } catch (error) { response = error; } if (response) { redirect( `http://localhost:3000/using-your-own-ui/sign-in/google-oauth?response=${JSON.stringify( response )}` ); } } ================================================ FILE: src/app/using-your-own-ui/sign-in/google-oauth/page.tsx ================================================ import { WorkOS } from '@workos-inc/node'; // This example uses Next.js with React Server Components. // Because this page is an RSC, the code stays on the server, which allows // us to use the WorkOS Node SDK without exposing our API key to the client. // // If your application is a single page app (SPA), you will need to: // - create a form that can POST to an endpoint in your backend // - call the `getAuthorizationURL` method in that endpoint // - redirect the user to the returned URL const workos = new WorkOS(process.env.WORKOS_API_KEY); export default function SignInWithGoogleOAuth({ searchParams, }: { searchParams: { [key: string]: string | string[] | undefined }; }) { const googleOAuthUrl = workos.userManagement.getAuthorizationUrl({ clientId: process.env.WORKOS_CLIENT_ID || '', provider: 'GoogleOAuth', redirectUri: 'http://localhost:3000/using-your-own-ui/sign-in/google-oauth/callback', }); const result = JSON.parse(String(searchParams.response ?? '{ "error": null }')); return (

Sign-in

Google OAuth

Continue with Google
{JSON.stringify(result, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/sign-in/magic-auth/magic-auth.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function sendCode(prevState: any, formData: FormData) { try { return await workos.userManagement.sendMagicAuthCode({ email: String(formData.get('email')), }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } export async function signIn(prevState: any, formData: FormData) { try { // For the sake of simplicity, we directly return the user here. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies. return await workos.userManagement.authenticateWithMagicAuth({ clientId: process.env.WORKOS_CLIENT_ID || '', code: String(formData.get('code')), email: String(formData.get('email')), }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } ================================================ FILE: src/app/using-your-own-ui/sign-in/magic-auth/page.tsx ================================================ 'use client'; import * as React from 'react'; import { useFormState } from 'react-dom'; import { sendCode, signIn } from './magic-auth'; export default function SignInWithMagicAuth() { // This example uses Next.js server actions to call functions on the server side. // // If your application is a single page app (SPA), you will need to: // - handle the form submission in `
` // - make an API call to your backend (e.g using `fetch`) const [sendCodeState, sendCodeAction] = useFormState(sendCode, { error: null }); const [signInState, signInAction] = useFormState(signIn, { error: null }); const [email, setEmail] = React.useState(''); if (sendCodeState?.error === null) { return (

Sign-in

Magic Auth

setEmail(event.target.value)} />
{JSON.stringify(sendCodeState, null, 2)}
); } return (

Sign-in

Magic Auth

{/* we need the email to authenticate with the code */}
{JSON.stringify(signInState, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/sign-in/microsoft-oauth/callback/route.ts ================================================ import { WorkOS } from '@workos-inc/node'; import { redirect } from 'next/navigation'; // This is a Next.js Route Handler. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle the request // - adapt the code below in your endpoint // // Please also note that for the sake of simplicity, we directly return the user here in the query string. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies. const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function GET(request: Request) { const code = new URL(request.url).searchParams.get('code') || ''; let response; try { response = await workos.userManagement.authenticateWithCode({ clientId: process.env.WORKOS_CLIENT_ID || '', code, }); } catch (error) { response = error; } if (response) { redirect( `http://localhost:3000/using-your-own-ui/sign-in/microsoft-oauth?response=${JSON.stringify( response )}` ); } } ================================================ FILE: src/app/using-your-own-ui/sign-in/microsoft-oauth/page.tsx ================================================ import { WorkOS } from '@workos-inc/node'; // This example uses Next.js with React Server Components. // Because this page is an RSC, the code stays on the server, which allows // us to use the WorkOS Node SDK without exposing our API key to the client. // // If your application is a single page app (SPA), you will need to: // - create a form that can POST to an endpoint in your backend // - call the `getAuthorizationURL` method in that endpoint // - redirect the user to the returned URL const workos = new WorkOS(process.env.WORKOS_API_KEY); export default function SignInWithMicrosoftOAuth({ searchParams, }: { searchParams: { [key: string]: string | string[] | undefined }; }) { const microsoftOAuthUrl = workos.userManagement.getAuthorizationUrl({ clientId: process.env.WORKOS_CLIENT_ID || '', provider: 'MicrosoftOAuth', redirectUri: 'http://localhost:3000/using-your-own-ui/sign-in/microsoft-oauth/callback', }); const result = JSON.parse(String(searchParams.response ?? '{ "error": null }')); return (

Sign-in

Microsoft OAuth

Continue with Microsoft
{JSON.stringify(result, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/sign-in/sso/callback/route.ts ================================================ import { WorkOS } from '@workos-inc/node'; import { redirect } from 'next/navigation'; // This is a Next.js Route Handler. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle the request // - adapt the code below in your endpoint // // Please also note that for the sake of simplicity, we directly return the user here in the query string. // In a real application, you would probably store the user in a token (JWT) // and store that token in your DB or use cookies. const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function GET(request: Request) { const code = new URL(request.url).searchParams.get('code') || ''; let response; try { response = await workos.userManagement.authenticateWithCode({ clientId: process.env.WORKOS_CLIENT_ID || '', code, }); } catch (error) { response = error; } if (response) { redirect( `http://localhost:3000/using-your-own-ui/sign-in/sso?response=${JSON.stringify(response)}` ); } } ================================================ FILE: src/app/using-your-own-ui/sign-in/sso/page.tsx ================================================ import { WorkOS } from '@workos-inc/node'; // This example uses Next.js with React Server Components. // Because this page is an RSC, the code stays on the server, which allows // us to use the WorkOS Node SDK without exposing our API key to the client. // // If your application is a single page app (SPA), you will need to: // - create a form that can POST to an endpoint in your backend // - call the `getAuthorizationURL` method in that endpoint // - redirect the user to the returned URL const workos = new WorkOS(process.env.WORKOS_API_KEY); export default function SignInWithSSO({ searchParams, }: { searchParams: { [key: string]: string | string[] | undefined }; }) { const ssoUrl = workos.userManagement.getAuthorizationUrl({ clientId: process.env.WORKOS_CLIENT_ID || '', organizationId: process.env.SSO_ENABLED_ORGANIZATION_ID || '', redirectUri: 'http://localhost:3000/using-your-own-ui/sign-in/sso/callback', }); const result = JSON.parse(String(searchParams.response ?? '{ "error": null }')); return (

Sign-in

Single Sign-On

Continue with SSO
{JSON.stringify(result, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/sign-up/email-password/email-password.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function signUp(prevState: any, formData: FormData) { try { // For the sake of simplicity, we directly return the user here. // In a real application, you would probably redirect the user to sign-in. return await workos.userManagement.createUser({ email: String(formData.get('email')), password: String(formData.get('password')), firstName: String(formData.get('firstName')), lastName: String(formData.get('lastName')), }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } ================================================ FILE: src/app/using-your-own-ui/sign-up/email-password/page.tsx ================================================ 'use client'; import { useFormState } from 'react-dom'; import { signUp } from './email-password'; export default function SignUpWithEmailPassword() { // This example uses Next.js server actions to call functions on the server side. // // If your application is a single page app (SPA), you will need to: // - handle the form submission in `
` // - make an API call to your backend (e.g using `fetch`) const [signUpState, signUpAction] = useFormState(signUp, { error: null }); return (

Sign-up

Email + Password

{JSON.stringify(signUpState, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/sign-up/magic-auth/magic-auth.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function signUp(prevState: any, formData: FormData) { try { // For the sake of simplicity, we directly return the user here. // In a real application, you would probably send a magic code email // and redirect the user to a page where they can enter the code. // See the `sign-in/magic-auth` example for more details. return await workos.userManagement.createUser({ email: String(formData.get('email')), password: undefined, firstName: String(formData.get('firstName')), lastName: String(formData.get('lastName')), }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } ================================================ FILE: src/app/using-your-own-ui/sign-up/magic-auth/page.tsx ================================================ 'use client'; import { useFormState } from 'react-dom'; import { signUp } from './magic-auth'; export default function SignUpWithMagicAuth() { // This example uses Next.js server actions to call functions on the server side. // // If your application is a single page app (SPA), you will need to: // - handle the form submission in `
` // - make an API call to your backend (e.g using `fetch`) const [signUpState, signUpAction] = useFormState(signUp, { error: null }); return (

Sign-up

Magic Auth

{JSON.stringify(signUpState, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/update-user/page.tsx ================================================ 'use client'; import { useFormState } from 'react-dom'; import { getUser, updateUser } from './update-user'; export default function UpdateUser() { // This example uses Next.js server actions to call functions on the server side. // // If your application is a single page app (SPA), you will need to: // - handle the form submission in `
` // - make an API call to your backend (e.g using `fetch`) const [getUserState, getUserAction] = useFormState(getUser, { error: null }); const [updateUserState, updateUserAction] = useFormState(updateUser, { error: null }); if (!('user' in getUserState)) { return (

Update user

{JSON.stringify(getUserState, null, 2)}
); } return (

Update user

{JSON.stringify(updateUserState, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/update-user/update-user.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; import type { User } from '@workos-inc/node'; import { revalidatePath } from 'next/cache'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function getUser(prevState: any, formData: FormData): Promise { try { const users = await workos.userManagement.listUsers({ email: String(formData.get('email')) }); const user = users.data[0]; return { user }; } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } export async function updateUser(prevState: any, formData: FormData): Promise { try { const user = await workos.userManagement.updateUser({ userId: String(formData.get('userId')), firstName: String(formData.get('firstName')), lastName: String(formData.get('lastName')), }); revalidatePath('/users-table'); return { user }; } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } type Response = { user: User } | { error: any }; ================================================ FILE: src/app/using-your-own-ui/users-table/loading.tsx ================================================ export default function Loading() { return (

Users

{Array.from({ length: 3 }, (_, i) => ( ))}
Email Name Verified
); } ================================================ FILE: src/app/using-your-own-ui/users-table/page.tsx ================================================ import { WorkOS } from '@workos-inc/node'; import type { User } from '@workos-inc/node'; import Link from 'next/link'; import { deleteUser } from './users-table'; // This example uses Next.js with React Server Components. // Because this page is an RSC, the code stays on the server, which allows // us to use the WorkOS Node SDK without exposing our API key to the client. // // If your application is a single page app (SPA), you will need to: // - create a backend endpoint to return the list of users // - adapt the code below in your endpoint // - make an API call to your backend (e.g using `fetch`) const workos = new WorkOS(process.env.WORKOS_API_KEY); export default async function UsersTable({ searchParams, }: { searchParams: { before?: string; after?: string }; }) { const users = await workos.userManagement.listUsers({ limit: 5, ...searchParams }); const { before, after } = users.listMetadata; return (

Users

{users.data.map((user) => ( ))}
Email Name Verified
{user.email} {formatName(user)} {user.emailVerified ? 'Yes' : 'No'}
); } function formatName(user: User) { if (user.firstName && user.lastName) { return `${user.firstName} ${user.lastName}`; } else { return user.lastName ?? user.firstName ?? ''; } } ================================================ FILE: src/app/using-your-own-ui/users-table/users-table.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; import { revalidatePath } from 'next/cache'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function deleteUser(formData: FormData) { try { await workos.userManagement.deleteUser(String(formData.get('userId'))); revalidatePath('/users-table'); } catch (error) { console.log(error); } } ================================================ FILE: src/app/using-your-own-ui/verify-email/page.tsx ================================================ 'use client'; import { useFormState } from 'react-dom'; import { sendCode, verifyEmail } from './verify-email'; export default function VerifyEmail() { // This example uses Next.js server actions to call functions on the server side. // // If your application is a single page app (SPA), you will need to: // - handle the form submission in `
` // - make an API call to your backend (e.g using `fetch`) const [sendCodeState, sendCodeAction] = useFormState(sendCode, { error: null }); const [verifyEmailState, verifyEmailAction] = useFormState(verifyEmail, { error: null }); if (!('user' in sendCodeState)) { return (

Verify email

{JSON.stringify(sendCodeState, null, 2)}
); } return (

Verify email

{JSON.stringify(verifyEmailState, null, 2)}
); } ================================================ FILE: src/app/using-your-own-ui/verify-email/verify-email.ts ================================================ 'use server'; // These are Next.js server actions. // // If your application is a single page app (SPA) with a separate backend you will need to: // - create a backend endpoint to handle each request // - adapt the code below in each of those endpoints // // Please also note that for the sake of simplicity, we return all errors here. // In a real application, you should pay attention to which errors make it // to the client for security reasons. import { WorkOS } from '@workos-inc/node'; import { revalidatePath } from 'next/cache'; const workos = new WorkOS(process.env.WORKOS_API_KEY); export async function sendCode(prevState: any, formData: FormData) { try { const users = await workos.userManagement.listUsers({ email: String(formData.get('email')) }); const user = users.data[0]; return await workos.userManagement.sendVerificationEmail({ userId: user.id }); } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } export async function verifyEmail(prevState: any, formData: FormData) { try { const response = await workos.userManagement.verifyEmail({ userId: String(formData.get('userId')), code: String(formData.get('code')), }); revalidatePath('/users-table'); return response; } catch (error) { return { error: JSON.parse(JSON.stringify(error)) }; } } ================================================ FILE: src/middleware.ts ================================================ // This file is only used in conjunction with the authkit-nextjs library import { authkitMiddleware } from '@workos-inc/authkit-nextjs'; export default authkitMiddleware({ debug: true }); // Match against pages that require auth, e.g.: export const config = { matcher: ['/using-hosted-authkit/with-nextjs'] }; ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./src/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] }