├── .env.local.example
├── .eslintrc.json
├── .github
└── workflows
│ ├── coana-analysis.yml
│ └── coana-guardrail.yml
├── .gitignore
├── .prettierrc
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── next.config.js
├── package.json
├── src
├── app
│ ├── back-link.tsx
│ ├── favicon.ico
│ ├── 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
└── yarn.lock
/.env.local.example:
--------------------------------------------------------------------------------
1 | # Retrieved from the WorkOS dashboard
2 | WORKOS_CLIENT_ID=
3 | WORKOS_API_KEY=
4 |
5 | # Needed for authkit-nextjs library example, defined in WorkOS dashboard
6 | WORKOS_REDIRECT_URI=
7 |
8 | # Needed for authkit-nextjs library example. Must be at least 32 characters long
9 | WORKOS_COOKIE_PASSWORD=
10 |
11 | # Only needed for the Single Sign-On example
12 | SSO_ENABLED_ORGANIZATION_ID=
13 |
14 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.github/workflows/coana-analysis.yml:
--------------------------------------------------------------------------------
1 | name: Coana Vulnerability Analysis
2 |
3 | on:
4 | schedule:
5 | - cron: "0 3 * * *" # every day at 3 AM
6 | workflow_dispatch:
7 | inputs:
8 | tags:
9 | description: "Manually run vulnerability analysis"
10 | # Required by the return-dispatch action
11 | distinct_id:
12 |
13 | jobs:
14 | coana-vulnerability-analysis:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout code
19 | uses: actions/checkout@v4
20 |
21 | - name: Run Coana CLI
22 | id: coana-cli
23 | uses: docker://coana/coana:latest@sha256:74144ed0fc9d7da87dcd45ccd12458cc7c25ad23e47eebd7ceb4860ed396d63e
24 | with:
25 | args: |
26 | coana run . \
27 | --api-key ${{ secrets.COANA_API_KEY }} \
28 | --repo-url https://github.com/${{github.repository}}
29 |
--------------------------------------------------------------------------------
/.github/workflows/coana-guardrail.yml:
--------------------------------------------------------------------------------
1 | name: Coana Guardrail
2 |
3 | on: pull_request
4 |
5 | jobs:
6 | guardrail:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - name: Checkout the ${{github.base_ref}} branch
11 | uses: actions/checkout@v4
12 | with:
13 | ref: ${{github.base_ref}} # checkout the base branch (usually master/main).
14 |
15 | - name: Fetch the PR branch
16 | run: |
17 | git fetch ${{ github.event.pull_request.head.repo.clone_url }} ${{ github.head_ref }}:${{ github.head_ref }} --depth=1
18 |
19 | - name: Get list of changed files relative to the main/master branch
20 | id: changed-files
21 | run: |
22 | echo "all_changed_files=$(git diff --name-only ${{ github.base_ref }} ${{ github.head_ref }} | tr '\n' ' ')" >> $GITHUB_OUTPUT
23 |
24 | - name: Use Node.js 20.x
25 | uses: actions/setup-node@v4
26 | with:
27 | node-version: 20.x
28 |
29 | - name: Run Coana on the ${{github.base_ref}} branch
30 | run: |
31 | npx @coana-tech/cli run . \
32 | --guardrail-mode \
33 | --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \
34 | -o /tmp/main-branch \
35 | --changed-files ${{ steps.changed-files.outputs.all_changed_files }} \
36 | --lightweight-reachability \
37 |
38 | # Reset file permissions.
39 | # This is necessary because the Coana CLI may add
40 | # new files with root ownership since it's using docker.
41 | # These files will not be deleted by the clean step in checkout
42 | # if the permissions are not reset.
43 | - name: Reset file permissions
44 | run: sudo chown -R $USER:$USER .
45 |
46 | - name: Checkout the current branch
47 | uses: actions/checkout@v4
48 | with:
49 | clean: true
50 |
51 | - name: Run Coana on the current branch
52 | run: |
53 | npx @coana-tech/cli run . \
54 | --guardrail-mode \
55 | --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \
56 | -o /tmp/current-branch \
57 | --changed-files ${{ steps.changed-files.outputs.all_changed_files }} \
58 | --lightweight-reachability \
59 |
60 | - name: Run Report Comparison
61 | run: |
62 | npx @coana-tech/cli compare-reports \
63 | --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \
64 | /tmp/main-branch/coana-report.json \
65 | /tmp/current-branch/coana-report.json
66 | env:
67 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | support@workos.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 WorkOS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
AuthKit
4 |
How to use AuthKit's hosted UI or build your own frontend with the headless User Management APIs
7 |
8 |
9 | ## Setup
10 |
11 | First, ensure you have set up the [environment variables](/#environment-variables) and [redirects](/#redirects).
12 |
13 | 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**.
14 |
15 | 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.
16 |
17 | 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.
18 |
19 | ## Examples
20 |
21 | - [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).
22 | - [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.
23 | - [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).
24 |
25 | ### Next.js
26 |
27 | For the `authkit-nextjs` example, you'll need to add the following to your environment variables:
28 |
29 | ```bash
30 | # Needed for authkit-nextjs library example, defined in WorkOS dashboard
31 | WORKOS_REDIRECT_URI=
32 |
33 | # Needed for authkit-nextjs library example. Must be at least 32 characters long
34 | WORKOS_COOKIE_PASSWORD=
35 | ```
36 |
37 | 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:
38 |
39 | ```bash
40 | openssl rand -base64 24
41 | ```
42 |
43 | ### Sessions
44 |
45 | 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.
46 |
47 | ```bash
48 | openssl rand -base64 32
49 | ```
50 |
51 | And update the `.env.local` file:
52 |
53 | ```bash
54 | # ...
55 | JWT_SECRET_KEY=""
56 | ```
57 |
--------------------------------------------------------------------------------
/src/app/using-hosted-authkit/basic/callback/route.ts:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 | import { redirect } from 'next/navigation';
3 |
4 | // This is a Next.js Route Handler.
5 | //
6 | // If your application is a single page app (SPA) with a separate backend you will need to:
7 | // - create a backend endpoint to handle the request
8 | // - adapt the code below in your endpoint
9 | //
10 | // Please also note that for the sake of simplicity, we directly return the user here in the query string.
11 | // In a real application, you would probably store the user in a token (JWT)
12 | // and store that token in your DB or use cookies (See `with-session` example for more details).
13 |
14 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
15 |
16 | export async function GET(request: Request) {
17 | const code = new URL(request.url).searchParams.get('code') || '';
18 |
19 | let response;
20 |
21 | try {
22 | response = await workos.userManagement.authenticateWithCode({
23 | clientId: process.env.WORKOS_CLIENT_ID || '',
24 | code,
25 | });
26 | } catch (error) {
27 | response = error;
28 | }
29 |
30 | if (response) {
31 | redirect(
32 | `http://localhost:3000/using-hosted-authkit/basic?response=${JSON.stringify(response)}`
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/using-hosted-authkit/basic/page.tsx:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 |
3 | // This example uses Next.js with React Server Components.
4 | // Because this page is an RSC, the code stays on the server, which allows
5 | // us to use the WorkOS Node SDK without exposing our API key to the client.
6 | //
7 | // If your application is a single page app (SPA), you will need to:
8 | // - create a form that can POST to an endpoint in your backend
9 | // - call the `getAuthorizationURL` method in that endpoint
10 | // - redirect the user to the returned URL
11 |
12 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
13 |
14 | export default function Basic({
15 | searchParams,
16 | }: {
17 | searchParams: { [key: string]: string | string[] | undefined };
18 | }) {
19 | const authKitUrl = workos.userManagement.getAuthorizationUrl({
20 | clientId: process.env.WORKOS_CLIENT_ID || '',
21 | provider: 'authkit',
22 | redirectUri: 'http://localhost:3000/using-hosted-authkit/basic/callback',
23 | });
24 |
25 | const result = JSON.parse(String(searchParams.response ?? '{ "error": null }'));
26 |
27 | return (
28 |
29 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/using-hosted-authkit/with-nextjs/callback/route.ts:
--------------------------------------------------------------------------------
1 | import { handleAuth } from '@workos-inc/authkit-nextjs';
2 |
3 | export const GET = handleAuth({ returnPathname: '/using-hosted-authkit/with-nextjs/' });
4 |
--------------------------------------------------------------------------------
/src/app/using-hosted-authkit/with-nextjs/page.tsx:
--------------------------------------------------------------------------------
1 | import { getSignInUrl, getUser, signOut } from '@workos-inc/authkit-nextjs';
2 |
3 | export default async function WithNextjs() {
4 | // Retrieves the user from the session or returns `null` if no user is signed in
5 | const { user } = await getUser();
6 |
7 | // Get the URL to redirect the user to AuthKit to sign in
8 | const signInUrl = await getSignInUrl();
9 |
10 | return (
11 |
12 |
Using hosted AuthKit
13 |
With Next.js library
14 | {user ? (
15 | <>
16 |
Welcome back {user?.firstName && `, ${user?.firstName}`}
7 |
8 |
9 | ## Setup
10 |
11 | First, ensure you have set up the [environment variables](/#environment-variables) and [redirects](/#redirects).
12 |
13 | 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**.
14 |
15 | 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.
16 |
17 | ## Examples
18 |
19 | ### Sign-up
20 |
21 | - [Email + Password](./sign-up/email-password/page.tsx)
22 | - [Magic Auth](./sign-up/magic-auth/page.tsx)
23 |
24 | ### Sign-in
25 |
26 | - [Email + Password](./sign-in/email-password/page.tsx)
27 | - [Magic Auth](./sign-in/magic-auth/page.tsx)
28 | - [Google OAuth](./sign-in/google-oauth/page.tsx)
29 | - [Microsoft OAuth](./sign-in/microsoft-oauth/page.tsx)
30 | - [Single Sign-On](./sign-in/sso/page.tsx)
31 |
32 | 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.
33 |
34 | 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.
35 |
36 | ```bash
37 | SSO_ENABLED_ORGANIZATION_ID=""
38 | ```
39 |
40 | ### Other
41 |
42 | - [Multi-Factor Auth](./mfa/page.tsx)
43 | - [Verify email](./verify-email/page.tsx)
44 | - [Reset password](./reset-password/page.tsx)
45 | - [Users table](./users-table/page.tsx)
46 | - [Update user](./update-user/page.tsx)
47 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/mfa/mfa.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | // These are Next.js server actions.
4 | //
5 | // If your application is a single page app (SPA) with a separate backend you will need to:
6 | // - create a backend endpoint to handle each request
7 | // - adapt the code below in each of those endpoints
8 | //
9 | // Please also note that for the sake of simplicity, we return all errors here.
10 | // In a real application, you should pay attention to which errors make it
11 | // to the client for security reasons.
12 |
13 | import { WorkOS } from '@workos-inc/node';
14 |
15 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
16 |
17 | export async function signIn(prevState: any, formData: FormData): Promise {
18 | try {
19 | // For the sake of simplicity, we directly return the user here.
20 | // In a real application, you would probably store the user in a token (JWT)
21 | // and store that token in your DB or use cookies.
22 | return await workos.userManagement.authenticateWithPassword({
23 | clientId: process.env.WORKOS_CLIENT_ID || '',
24 | email: String(formData.get('email')),
25 | password: String(formData.get('password')),
26 | });
27 | } catch (error) {
28 | const err = JSON.parse(JSON.stringify(error));
29 |
30 | if (err.rawData.code === 'mfa_enrollment') {
31 | const { authenticationFactor, authenticationChallenge } =
32 | await workos.userManagement.enrollAuthFactor({
33 | userId: err.rawData.user.id,
34 | type: 'totp',
35 | totpIssuer: 'WorkOS',
36 | totpUser: err.rawData.user.email,
37 | });
38 | return {
39 | authenticationFactor,
40 | authenticationChallenge,
41 | pendingAuthenticationToken: err.rawData.pending_authentication_token,
42 | };
43 | }
44 |
45 | if (err.rawData.code === 'mfa_challenge') {
46 | const challenge = await workos.mfa.challengeFactor({
47 | authenticationFactorId: err.rawData.authentication_factors[0].id,
48 | });
49 | return {
50 | authenticationChallenge: challenge,
51 | pendingAuthenticationToken: err.rawData.pending_authentication_token,
52 | };
53 | }
54 |
55 | return { error: err };
56 | }
57 | }
58 |
59 | export async function verifyTotp(prevState: any, formData: FormData) {
60 | try {
61 | // For the sake of simplicity, we directly return the user here.
62 | // In a real application, you would probably store the user in a token (JWT)
63 | // and store that token in your DB or use cookies.
64 | return await workos.userManagement.authenticateWithTotp({
65 | clientId: process.env.WORKOS_CLIENT_ID || '',
66 | authenticationChallengeId: String(formData.get('authenticationChallengeId')),
67 | pendingAuthenticationToken: String(formData.get('pendingAuthenticationToken')),
68 | code: String(formData.get('code')),
69 | });
70 | } catch (error) {
71 | return { error: JSON.parse(JSON.stringify(error)) };
72 | }
73 | }
74 |
75 | type UnpackPromise = T extends Promise ? U : T;
76 | type AuthenticateResponse = UnpackPromise<
77 | ReturnType
78 | >;
79 | type EnrollResponse = UnpackPromise>;
80 | type SignInResponse =
81 | | AuthenticateResponse
82 | | {
83 | authenticationFactor?: EnrollResponse['authenticationFactor'];
84 | authenticationChallenge: EnrollResponse['authenticationChallenge'];
85 | pendingAuthenticationToken: string;
86 | }
87 | | { error: any };
88 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/mfa/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useFormState } from 'react-dom';
4 | import { signIn, verifyTotp } from './mfa';
5 | import Image from 'next/image';
6 |
7 | export default function Mfa() {
8 | // This example uses Next.js server actions to call functions on the server side.
9 | //
10 | // If your application is a single page app (SPA), you will need to:
11 | // - handle the form submission in `
`
17 | // - make an API call to your backend (e.g using `fetch`)
18 | const [sendResetState, sendResetAction] = useFormState(sendReset, { error: null });
19 | const [resetPasswordState, resetPasswordAction] = useFormState(resetPassword, { error: null });
20 |
21 | if (!token) {
22 | return (
23 |
24 |
Reset password
25 |
26 |
27 |
28 |
29 |
38 |
39 |
40 |
41 |
42 |
43 |
{JSON.stringify(sendResetState, null, 2)}
44 |
45 | );
46 | }
47 |
48 | return (
49 |
50 |
Reset password
51 |
52 |
53 |
54 |
55 |
64 |
65 |
66 |
67 |
68 | {email && (
69 | // We also include the email in a hidden input so that password managers can update the password on the correct account.
70 | // https://developer.1password.com/docs/web/compatible-website-design/#password-change-and-reset-forms
71 |
78 | )}
79 |
80 |
81 |
82 |
83 |
{JSON.stringify(resetPasswordState, null, 2)}
84 |
85 | );
86 | }
87 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/reset-password/reset-password.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | // These are Next.js server actions.
4 | //
5 | // If your application is a single page app (SPA) with a separate backend you will need to:
6 | // - create a backend endpoint to handle each request
7 | // - adapt the code below in each of those endpoints
8 | //
9 | // Please also note that for the sake of simplicity, we return all errors here.
10 | // In a real application, you should pay attention to which errors make it
11 | // to the client for security reasons.
12 |
13 | import { WorkOS } from '@workos-inc/node';
14 |
15 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
16 |
17 | export async function sendReset(prevState: any, formData: FormData) {
18 | try {
19 | const email = String(formData.get('email'));
20 | return await workos.userManagement.sendPasswordResetEmail({
21 | email,
22 | passwordResetUrl: `http://localhost:3000/using-your-own-ui/reset-password?email=${email}`,
23 | });
24 | } catch (error) {
25 | return { error: JSON.parse(JSON.stringify(error)) };
26 | }
27 | }
28 |
29 | export async function resetPassword(prevState: any, formData: FormData) {
30 | try {
31 | return await workos.userManagement.resetPassword({
32 | newPassword: String(formData.get('newPassword')),
33 | token: String(formData.get('token')),
34 | });
35 | } catch (error) {
36 | return { error: JSON.parse(JSON.stringify(error)) };
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/email-password/email-password.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | // These are Next.js server actions.
4 | //
5 | // If your application is a single page app (SPA) with a separate backend you will need to:
6 | // - create a backend endpoint to handle each request
7 | // - adapt the code below in each of those endpoints
8 | //
9 | // Please also note that for the sake of simplicity, we return all errors here.
10 | // In a real application, you should pay attention to which errors make it
11 | // to the client for security reasons.
12 |
13 | import { WorkOS } from '@workos-inc/node';
14 |
15 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
16 |
17 | export async function signIn(prevState: any, formData: FormData) {
18 | try {
19 | // For the sake of simplicity, we directly return the user here.
20 | // In a real application, you would probably store the user in a token (JWT)
21 | // and store that token in your DB or use cookies.
22 | return await workos.userManagement.authenticateWithPassword({
23 | clientId: process.env.WORKOS_CLIENT_ID || '',
24 | email: String(formData.get('email')),
25 | password: String(formData.get('password')),
26 | });
27 | } catch (error) {
28 | return { error: JSON.parse(JSON.stringify(error)) };
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/email-password/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useFormState } from 'react-dom';
4 | import { signIn } from './email-password';
5 |
6 | export default function SignInWithEmailPassword() {
7 | // This example uses Next.js server actions to call functions on the server side.
8 | //
9 | // If your application is a single page app (SPA), you will need to:
10 | // - handle the form submission in `
`
11 | // - make an API call to your backend (e.g using `fetch`)
12 | const [signInState, signInAction] = useFormState(signIn, { error: null });
13 |
14 | return (
15 |
16 |
Sign-in
17 |
Email + Password
18 |
19 |
20 |
21 |
22 |
31 |
32 |
33 |
34 |
35 |
43 |
44 |
45 |
46 |
47 |
48 |
{JSON.stringify(signInState, null, 2)}
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/github-oauth/callback/route.ts:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 | import { redirect } from 'next/navigation';
3 |
4 | // This is a Next.js Route Handler.
5 | //
6 | // If your application is a single page app (SPA) with a separate backend you will need to:
7 | // - create a backend endpoint to handle the request
8 | // - adapt the code below in your endpoint
9 | //
10 | // Please also note that for the sake of simplicity, we directly return the user here in the query string.
11 | // In a real application, you would probably store the user in a token (JWT)
12 | // and store that token in your DB or use cookies.
13 |
14 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
15 |
16 | export async function GET(request: Request) {
17 | const code = new URL(request.url).searchParams.get('code') || '';
18 |
19 | let response;
20 |
21 | try {
22 | response = await workos.userManagement.authenticateWithCode({
23 | clientId: process.env.WORKOS_CLIENT_ID || '',
24 | code,
25 | });
26 | } catch (error) {
27 | response = error;
28 | }
29 |
30 | if (response) {
31 | redirect(
32 | `http://localhost:3000/using-your-own-ui/sign-in/github-oauth?response=${JSON.stringify(
33 | response
34 | )}`
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/github-oauth/page.tsx:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 |
3 | // This example uses Next.js with React Server Components.
4 | // Because this page is an RSC, the code stays on the server, which allows
5 | // us to use the WorkOS Node SDK without exposing our API key to the client.
6 | //
7 | // If your application is a single page app (SPA), you will need to:
8 | // - create a form that can POST to an endpoint in your backend
9 | // - call the `getAuthorizationURL` method in that endpoint
10 | // - redirect the user to the returned URL
11 |
12 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
13 |
14 | export default function SignInWithGitHubOAuth({
15 | searchParams,
16 | }: {
17 | searchParams: { [key: string]: string | string[] | undefined };
18 | }) {
19 | const githubOAuthUrl = workos.userManagement.getAuthorizationUrl({
20 | clientId: process.env.WORKOS_CLIENT_ID || '',
21 | provider: 'GitHubOAuth',
22 | redirectUri: 'http://localhost:3000/using-your-own-ui/sign-in/github-oauth/callback',
23 | });
24 |
25 | const result = JSON.parse(String(searchParams.response ?? '{ "error": null }'));
26 |
27 | return (
28 |
29 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/google-oauth/callback/route.ts:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 | import { redirect } from 'next/navigation';
3 |
4 | // This is a Next.js Route Handler.
5 | //
6 | // If your application is a single page app (SPA) with a separate backend you will need to:
7 | // - create a backend endpoint to handle the request
8 | // - adapt the code below in your endpoint
9 | //
10 | // Please also note that for the sake of simplicity, we directly return the user here in the query string.
11 | // In a real application, you would probably store the user in a token (JWT)
12 | // and store that token in your DB or use cookies.
13 |
14 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
15 |
16 | export async function GET(request: Request) {
17 | const code = new URL(request.url).searchParams.get('code') || '';
18 |
19 | let response;
20 |
21 | try {
22 | response = await workos.userManagement.authenticateWithCode({
23 | clientId: process.env.WORKOS_CLIENT_ID || '',
24 | code,
25 | });
26 | } catch (error) {
27 | response = error;
28 | }
29 |
30 | if (response) {
31 | redirect(
32 | `http://localhost:3000/using-your-own-ui/sign-in/google-oauth?response=${JSON.stringify(
33 | response
34 | )}`
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/google-oauth/page.tsx:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 |
3 | // This example uses Next.js with React Server Components.
4 | // Because this page is an RSC, the code stays on the server, which allows
5 | // us to use the WorkOS Node SDK without exposing our API key to the client.
6 | //
7 | // If your application is a single page app (SPA), you will need to:
8 | // - create a form that can POST to an endpoint in your backend
9 | // - call the `getAuthorizationURL` method in that endpoint
10 | // - redirect the user to the returned URL
11 |
12 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
13 |
14 | export default function SignInWithGoogleOAuth({
15 | searchParams,
16 | }: {
17 | searchParams: { [key: string]: string | string[] | undefined };
18 | }) {
19 | const googleOAuthUrl = workos.userManagement.getAuthorizationUrl({
20 | clientId: process.env.WORKOS_CLIENT_ID || '',
21 | provider: 'GoogleOAuth',
22 | redirectUri: 'http://localhost:3000/using-your-own-ui/sign-in/google-oauth/callback',
23 | });
24 |
25 | const result = JSON.parse(String(searchParams.response ?? '{ "error": null }'));
26 |
27 | return (
28 |
29 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/magic-auth/magic-auth.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | // These are Next.js server actions.
4 | //
5 | // If your application is a single page app (SPA) with a separate backend you will need to:
6 | // - create a backend endpoint to handle each request
7 | // - adapt the code below in each of those endpoints
8 | //
9 | // Please also note that for the sake of simplicity, we return all errors here.
10 | // In a real application, you should pay attention to which errors make it
11 | // to the client for security reasons.
12 |
13 | import { WorkOS } from '@workos-inc/node';
14 |
15 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
16 |
17 | export async function sendCode(prevState: any, formData: FormData) {
18 | try {
19 | return await workos.userManagement.sendMagicAuthCode({
20 | email: String(formData.get('email')),
21 | });
22 | } catch (error) {
23 | return { error: JSON.parse(JSON.stringify(error)) };
24 | }
25 | }
26 |
27 | export async function signIn(prevState: any, formData: FormData) {
28 | try {
29 | // For the sake of simplicity, we directly return the user here.
30 | // In a real application, you would probably store the user in a token (JWT)
31 | // and store that token in your DB or use cookies.
32 | return await workos.userManagement.authenticateWithMagicAuth({
33 | clientId: process.env.WORKOS_CLIENT_ID || '',
34 | code: String(formData.get('code')),
35 | email: String(formData.get('email')),
36 | });
37 | } catch (error) {
38 | return { error: JSON.parse(JSON.stringify(error)) };
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/magic-auth/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import * as React from 'react';
4 | import { useFormState } from 'react-dom';
5 | import { sendCode, signIn } from './magic-auth';
6 |
7 | export default function SignInWithMagicAuth() {
8 | // This example uses Next.js server actions to call functions on the server side.
9 | //
10 | // If your application is a single page app (SPA), you will need to:
11 | // - handle the form submission in `
`
12 | // - make an API call to your backend (e.g using `fetch`)
13 | const [sendCodeState, sendCodeAction] = useFormState(sendCode, { error: null });
14 | const [signInState, signInAction] = useFormState(signIn, { error: null });
15 | const [email, setEmail] = React.useState('');
16 |
17 | if (sendCodeState?.error === null) {
18 | return (
19 |
20 |
65 |
66 | {/* we need the email to authenticate with the code */}
67 |
68 |
69 |
70 |
71 |
72 |
{JSON.stringify(signInState, null, 2)}
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/microsoft-oauth/callback/route.ts:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 | import { redirect } from 'next/navigation';
3 |
4 | // This is a Next.js Route Handler.
5 | //
6 | // If your application is a single page app (SPA) with a separate backend you will need to:
7 | // - create a backend endpoint to handle the request
8 | // - adapt the code below in your endpoint
9 | //
10 | // Please also note that for the sake of simplicity, we directly return the user here in the query string.
11 | // In a real application, you would probably store the user in a token (JWT)
12 | // and store that token in your DB or use cookies.
13 |
14 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
15 |
16 | export async function GET(request: Request) {
17 | const code = new URL(request.url).searchParams.get('code') || '';
18 |
19 | let response;
20 |
21 | try {
22 | response = await workos.userManagement.authenticateWithCode({
23 | clientId: process.env.WORKOS_CLIENT_ID || '',
24 | code,
25 | });
26 | } catch (error) {
27 | response = error;
28 | }
29 |
30 | if (response) {
31 | redirect(
32 | `http://localhost:3000/using-your-own-ui/sign-in/microsoft-oauth?response=${JSON.stringify(
33 | response
34 | )}`
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/microsoft-oauth/page.tsx:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 |
3 | // This example uses Next.js with React Server Components.
4 | // Because this page is an RSC, the code stays on the server, which allows
5 | // us to use the WorkOS Node SDK without exposing our API key to the client.
6 | //
7 | // If your application is a single page app (SPA), you will need to:
8 | // - create a form that can POST to an endpoint in your backend
9 | // - call the `getAuthorizationURL` method in that endpoint
10 | // - redirect the user to the returned URL
11 |
12 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
13 |
14 | export default function SignInWithMicrosoftOAuth({
15 | searchParams,
16 | }: {
17 | searchParams: { [key: string]: string | string[] | undefined };
18 | }) {
19 | const microsoftOAuthUrl = workos.userManagement.getAuthorizationUrl({
20 | clientId: process.env.WORKOS_CLIENT_ID || '',
21 | provider: 'MicrosoftOAuth',
22 | redirectUri: 'http://localhost:3000/using-your-own-ui/sign-in/microsoft-oauth/callback',
23 | });
24 |
25 | const result = JSON.parse(String(searchParams.response ?? '{ "error": null }'));
26 |
27 | return (
28 |
29 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/sso/callback/route.ts:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 | import { redirect } from 'next/navigation';
3 |
4 | // This is a Next.js Route Handler.
5 | //
6 | // If your application is a single page app (SPA) with a separate backend you will need to:
7 | // - create a backend endpoint to handle the request
8 | // - adapt the code below in your endpoint
9 | //
10 | // Please also note that for the sake of simplicity, we directly return the user here in the query string.
11 | // In a real application, you would probably store the user in a token (JWT)
12 | // and store that token in your DB or use cookies.
13 |
14 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
15 |
16 | export async function GET(request: Request) {
17 | const code = new URL(request.url).searchParams.get('code') || '';
18 |
19 | let response;
20 |
21 | try {
22 | response = await workos.userManagement.authenticateWithCode({
23 | clientId: process.env.WORKOS_CLIENT_ID || '',
24 | code,
25 | });
26 | } catch (error) {
27 | response = error;
28 | }
29 |
30 | if (response) {
31 | redirect(
32 | `http://localhost:3000/using-your-own-ui/sign-in/sso?response=${JSON.stringify(response)}`
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-in/sso/page.tsx:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 |
3 | // This example uses Next.js with React Server Components.
4 | // Because this page is an RSC, the code stays on the server, which allows
5 | // us to use the WorkOS Node SDK without exposing our API key to the client.
6 | //
7 | // If your application is a single page app (SPA), you will need to:
8 | // - create a form that can POST to an endpoint in your backend
9 | // - call the `getAuthorizationURL` method in that endpoint
10 | // - redirect the user to the returned URL
11 |
12 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
13 |
14 | export default function SignInWithSSO({
15 | searchParams,
16 | }: {
17 | searchParams: { [key: string]: string | string[] | undefined };
18 | }) {
19 | const ssoUrl = workos.userManagement.getAuthorizationUrl({
20 | clientId: process.env.WORKOS_CLIENT_ID || '',
21 | organizationId: process.env.SSO_ENABLED_ORGANIZATION_ID || '',
22 | redirectUri: 'http://localhost:3000/using-your-own-ui/sign-in/sso/callback',
23 | });
24 |
25 | const result = JSON.parse(String(searchParams.response ?? '{ "error": null }'));
26 |
27 | return (
28 |
29 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-up/email-password/email-password.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | // These are Next.js server actions.
4 | //
5 | // If your application is a single page app (SPA) with a separate backend you will need to:
6 | // - create a backend endpoint to handle each request
7 | // - adapt the code below in each of those endpoints
8 | //
9 | // Please also note that for the sake of simplicity, we return all errors here.
10 | // In a real application, you should pay attention to which errors make it
11 | // to the client for security reasons.
12 |
13 | import { WorkOS } from '@workos-inc/node';
14 |
15 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
16 |
17 | export async function signUp(prevState: any, formData: FormData) {
18 | try {
19 | // For the sake of simplicity, we directly return the user here.
20 | // In a real application, you would probably redirect the user to sign-in.
21 | return await workos.userManagement.createUser({
22 | email: String(formData.get('email')),
23 | password: String(formData.get('password')),
24 | firstName: String(formData.get('firstName')),
25 | lastName: String(formData.get('lastName')),
26 | });
27 | } catch (error) {
28 | return { error: JSON.parse(JSON.stringify(error)) };
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-up/email-password/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useFormState } from 'react-dom';
4 | import { signUp } from './email-password';
5 |
6 | export default function SignUpWithEmailPassword() {
7 | // This example uses Next.js server actions to call functions on the server side.
8 | //
9 | // If your application is a single page app (SPA), you will need to:
10 | // - handle the form submission in `
`
11 | // - make an API call to your backend (e.g using `fetch`)
12 | const [signUpState, signUpAction] = useFormState(signUp, { error: null });
13 |
14 | return (
15 |
16 |
Sign-up
17 |
Email + Password
18 |
19 |
20 |
21 |
22 |
31 |
32 |
33 |
34 |
35 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
{JSON.stringify(signUpState, null, 2)}
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-up/magic-auth/magic-auth.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | // These are Next.js server actions.
4 | //
5 | // If your application is a single page app (SPA) with a separate backend you will need to:
6 | // - create a backend endpoint to handle each request
7 | // - adapt the code below in each of those endpoints
8 | //
9 | // Please also note that for the sake of simplicity, we return all errors here.
10 | // In a real application, you should pay attention to which errors make it
11 | // to the client for security reasons.
12 |
13 | import { WorkOS } from '@workos-inc/node';
14 |
15 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
16 |
17 | export async function signUp(prevState: any, formData: FormData) {
18 | try {
19 | // For the sake of simplicity, we directly return the user here.
20 | // In a real application, you would probably send a magic code email
21 | // and redirect the user to a page where they can enter the code.
22 | // See the `sign-in/magic-auth` example for more details.
23 | return await workos.userManagement.createUser({
24 | email: String(formData.get('email')),
25 | password: undefined,
26 | firstName: String(formData.get('firstName')),
27 | lastName: String(formData.get('lastName')),
28 | });
29 | } catch (error) {
30 | return { error: JSON.parse(JSON.stringify(error)) };
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/sign-up/magic-auth/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useFormState } from 'react-dom';
4 | import { signUp } from './magic-auth';
5 |
6 | export default function SignUpWithMagicAuth() {
7 | // This example uses Next.js server actions to call functions on the server side.
8 | //
9 | // If your application is a single page app (SPA), you will need to:
10 | // - handle the form submission in `
`
11 | // - make an API call to your backend (e.g using `fetch`)
12 | const [signUpState, signUpAction] = useFormState(signUp, { error: null });
13 |
14 | return (
15 |
16 |
Sign-up
17 |
Magic Auth
18 |
19 |
20 |
21 |
22 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
{JSON.stringify(signUpState, null, 2)}
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/update-user/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useFormState } from 'react-dom';
4 | import { getUser, updateUser } from './update-user';
5 |
6 | export default function UpdateUser() {
7 | // This example uses Next.js server actions to call functions on the server side.
8 | //
9 | // If your application is a single page app (SPA), you will need to:
10 | // - handle the form submission in `
`
11 | // - make an API call to your backend (e.g using `fetch`)
12 | const [getUserState, getUserAction] = useFormState(getUser, { error: null });
13 | const [updateUserState, updateUserAction] = useFormState(updateUser, { error: null });
14 |
15 | if (!('user' in getUserState)) {
16 | return (
17 |
18 |
Update user
19 |
20 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
{JSON.stringify(getUserState, null, 2)}
38 |
39 | );
40 | }
41 |
42 | return (
43 |
44 |
Update user
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
62 |
63 |
64 |
65 |
66 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
{JSON.stringify(updateUserState, null, 2)}
81 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/update-user/update-user.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | // These are Next.js server actions.
4 | //
5 | // If your application is a single page app (SPA) with a separate backend you will need to:
6 | // - create a backend endpoint to handle each request
7 | // - adapt the code below in each of those endpoints
8 | //
9 | // Please also note that for the sake of simplicity, we return all errors here.
10 | // In a real application, you should pay attention to which errors make it
11 | // to the client for security reasons.
12 |
13 | import { WorkOS } from '@workos-inc/node';
14 | import type { User } from '@workos-inc/node';
15 | import { revalidatePath } from 'next/cache';
16 |
17 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
18 |
19 | export async function getUser(prevState: any, formData: FormData): Promise {
20 | try {
21 | const users = await workos.userManagement.listUsers({ email: String(formData.get('email')) });
22 | const user = users.data[0];
23 | return { user };
24 | } catch (error) {
25 | return { error: JSON.parse(JSON.stringify(error)) };
26 | }
27 | }
28 |
29 | export async function updateUser(prevState: any, formData: FormData): Promise {
30 | try {
31 | const user = await workos.userManagement.updateUser({
32 | userId: String(formData.get('userId')),
33 | firstName: String(formData.get('firstName')),
34 | lastName: String(formData.get('lastName')),
35 | });
36 | revalidatePath('/users-table');
37 | return { user };
38 | } catch (error) {
39 | return { error: JSON.parse(JSON.stringify(error)) };
40 | }
41 | }
42 |
43 | type Response = { user: User } | { error: any };
44 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/users-table/loading.tsx:
--------------------------------------------------------------------------------
1 | export default function Loading() {
2 | return (
3 |
4 |
28 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/users-table/page.tsx:
--------------------------------------------------------------------------------
1 | import { WorkOS } from '@workos-inc/node';
2 | import type { User } from '@workos-inc/node';
3 | import Link from 'next/link';
4 | import { deleteUser } from './users-table';
5 |
6 | // This example uses Next.js with React Server Components.
7 | // Because this page is an RSC, the code stays on the server, which allows
8 | // us to use the WorkOS Node SDK without exposing our API key to the client.
9 | //
10 | // If your application is a single page app (SPA), you will need to:
11 | // - create a backend endpoint to return the list of users
12 | // - adapt the code below in your endpoint
13 | // - make an API call to your backend (e.g using `fetch`)
14 |
15 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
16 |
17 | export default async function UsersTable({
18 | searchParams,
19 | }: {
20 | searchParams: { before?: string; after?: string };
21 | }) {
22 | const users = await workos.userManagement.listUsers({ limit: 5, ...searchParams });
23 | const { before, after } = users.listMetadata;
24 |
25 | return (
26 |
27 |
Users
28 |
29 |
30 |
31 |
32 |
Email
33 |
Name
34 |
Verified
35 |
36 |
37 |
38 |
39 | {users.data.map((user) => (
40 |
41 |
{user.email}
42 |
{formatName(user)}
43 |
{user.emailVerified ? 'Yes' : 'No'}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | ))}
52 |
53 |
54 |
55 |
59 |
60 | );
61 | }
62 |
63 | function formatName(user: User) {
64 | if (user.firstName && user.lastName) {
65 | return `${user.firstName} ${user.lastName}`;
66 | } else {
67 | return user.lastName ?? user.firstName ?? '';
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/users-table/users-table.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | // These are Next.js server actions.
4 | //
5 | // If your application is a single page app (SPA) with a separate backend you will need to:
6 | // - create a backend endpoint to handle each request
7 | // - adapt the code below in each of those endpoints
8 | //
9 | // Please also note that for the sake of simplicity, we return all errors here.
10 | // In a real application, you should pay attention to which errors make it
11 | // to the client for security reasons.
12 |
13 | import { WorkOS } from '@workos-inc/node';
14 | import { revalidatePath } from 'next/cache';
15 |
16 | const workos = new WorkOS(process.env.WORKOS_API_KEY);
17 |
18 | export async function deleteUser(formData: FormData) {
19 | try {
20 | await workos.userManagement.deleteUser(String(formData.get('userId')));
21 | revalidatePath('/users-table');
22 | } catch (error) {
23 | console.log(error);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/using-your-own-ui/verify-email/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useFormState } from 'react-dom';
4 | import { sendCode, verifyEmail } from './verify-email';
5 |
6 | export default function VerifyEmail() {
7 | // This example uses Next.js server actions to call functions on the server side.
8 | //
9 | // If your application is a single page app (SPA), you will need to:
10 | // - handle the form submission in `
`
11 | // - make an API call to your backend (e.g using `fetch`)
12 | const [sendCodeState, sendCodeAction] = useFormState(sendCode, { error: null });
13 | const [verifyEmailState, verifyEmailAction] = useFormState(verifyEmail, { error: null });
14 |
15 | if (!('user' in sendCodeState)) {
16 | return (
17 |
18 |