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
================================================
<p align="center">
<img src="https://github.com/workos/authkit/assets/896475/9fa7a91e-f5a8-4922-96fb-20a7b478d075" width="72" />
<h1 align="center">AuthKit</h1>
<p align="center">How to use AuthKit's hosted UI or build your own frontend with the headless User Management APIs</p>
<p align="center"><a href="https://workos.com/docs/user-management">Explore the docs ↗</a></strong></p>
</p>
<p align="center">
<img alt="Screenshot of hosted UI AuthKit in light mode" src="https://github.com/workos/authkit/assets/108872335/200931ff-51fc-4825-894d-696dd17b88f6">
</p>
## 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="<your Client ID>"
WORKOS_API_KEY="<your Secret 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 <Link href="/">Home</Link>;
return <Link href={`/${segments[0]}`}>Examples</Link>;
}
================================================
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 (
<html lang="en">
<body className={inter.className}>
<BackLink />
{children}
</body>
</html>
);
}
================================================
FILE: src/app/page.tsx
================================================
import Link from 'next/link';
export default function Home() {
return (
<main>
<h1>Examples</h1>
<ul>
<li>
<Link href="/using-your-own-ui">Using your own UI</Link>
</li>
<li>
<Link href="/using-hosted-authkit">Using hosted AuthKit</Link>
</li>
</ul>
</main>
);
}
================================================
FILE: src/app/using-hosted-authkit/README.md
================================================
<p align="center">
<img src="https://github.com/workos/authkit/assets/896475/9fa7a91e-f5a8-4922-96fb-20a7b478d075" width="72" />
<h1 align="center">Using AuthKit's hosted UI</h1>
<p align="center">How to use AuthKit with themeable hosted UI</p>
<p align="center"><strong><a href="#examples">View the examples</a></strong> · <strong><a href="https://workos.com/docs/user-management">Explore the docs ↗</a></strong></p>
<br><br><br>
</p>
## 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="<your JTW secret>"
```
================================================
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 (
<main>
<h1>Using hosted AuthKit</h1>
<h2>Basic example</h2>
<a href={authKitUrl}>Sign-in with AuthKit</a>
<pre>{JSON.stringify(result, null, 2)}</pre>
</main>
);
}
================================================
FILE: src/app/using-hosted-authkit/page.tsx
================================================
import Link from 'next/link';
export default function UsingHostedAuthKit() {
return (
<main>
<h1>Using hosted AuthKit</h1>
<ul>
<li>
<Link href="/using-hosted-authkit/basic">Basic example</Link>
</li>
<li>
<Link href="/using-hosted-authkit/with-nextjs">With Next.js library</Link>
</li>
<li>
<Link href="/using-hosted-authkit/with-session">With session</Link>
</li>
</ul>
</main>
);
}
================================================
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 (
<main>
<h1>Using hosted AuthKit</h1>
<h2>With Next.js library</h2>
{user ? (
<>
<p>Welcome back {user?.firstName && `, ${user?.firstName}`}</p>
<form
action={async () => {
'use server';
await signOut();
}}
>
<button type="submit">Sign out</button>
</form>
</>
) : (
<a href={signInUrl}>Sign in</a>
)}
<pre>{JSON.stringify(user, null, 2)}</pre>
</main>
);
}
================================================
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 (
<main>
<h1>With session</h1>
{isAuthenticated ? (
<>
<h2>Welcome back{user?.firstName && `, ${user?.firstName}`}</h2>
<p>You are now authenticated into the application.</p>
<form
action={async () => {
'use server';
await signOut();
}}
>
<button type="submit">Sign-out</button>
</form>
</>
) : (
<>
<h2>Sign-in</h2>
<p>Sign-in to view your account details</p>
<a href={authKitUrl}>Sign-in</a>
</>
)}
<pre>{JSON.stringify({ user }, null, 2)}</pre>
</main>
);
}
================================================
FILE: src/app/using-your-own-ui/README.md
================================================
<p align="center">
<img src="https://github.com/workos/authkit/assets/896475/9fa7a91e-f5a8-4922-96fb-20a7b478d075" width="72" />
<h1 align="center">Using AuthKit with your own UI</h1>
<p align="center">How to use AuthKit with your own frontend using headless User Management APIs</p>
<p align="center"><strong><a href="#examples">View the examples</a></strong> · <strong><a href="https://workos.com/docs/user-management">Explore the docs ↗</a></strong></p>
<br><br><br>
</p>
## 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="<your 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<SignInResponse> {
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> = T extends Promise<infer U> ? U : T;
type AuthenticateResponse = UnpackPromise<
ReturnType<typeof workos.userManagement.authenticateWithPassword>
>;
type EnrollResponse = UnpackPromise<ReturnType<typeof workos.userManagement.enrollAuthFactor>>;
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 `<form onSubmit>`
// - 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 (
<main key="sign-in">
<h1>Multi-Factor Auth</h1>
<h2>Sign-in</h2>
<form action={signInAction}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
autoCapitalize="off"
autoComplete="username"
autoFocus
required
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="password"
name="password"
id="password"
autoCapitalize="off"
autoComplete="current-password"
required
/>
</div>
<button type="submit">Sign-in</button>
</form>
<pre>{JSON.stringify(signInState, null, 2)}</pre>
</main>
);
}
return (
<main key="mfa">
<h1>Multi-Factor Auth</h1>
{signInState.authenticationFactor ? (
<>
<h2>Enroll</h2>
<p>Scan the QR code</p>
<Image
src={signInState.authenticationFactor.totp.qrCode}
width="160"
height="160"
alt="QR code"
/>
<p>then</p>
</>
) : (
<h2>Verify</h2>
)}
<form action={verifyAction}>
<div>
<label htmlFor="code">Enter the code from your app</label>
<input
type="text"
name="code"
id="code"
inputMode="numeric"
autoComplete="one-time-code"
pattern="^\d{6}$"
autoFocus
required
/>
</div>
<input
type="hidden"
name="authenticationChallengeId"
value={signInState.authenticationChallenge.id}
/>
<input
type="hidden"
name="pendingAuthenticationToken"
value={signInState.pendingAuthenticationToken}
/>
<button type="submit">Continue</button>
</form>
<pre>{JSON.stringify(verifyState, null, 2)}</pre>
</main>
);
}
================================================
FILE: src/app/using-your-own-ui/page.tsx
================================================
import Link from 'next/link';
export default function UsingYourOwnUi() {
return (
<main>
<h1>Using your own UI</h1>
<h2>Sign-up</h2>
<ul>
<li>
<Link href="/using-your-own-ui/sign-up/email-password">Email + Password</Link>
</li>
<li>
<Link href="/using-your-own-ui/sign-up/magic-auth">Magic Auth</Link>
</li>
</ul>
<h2>Sign-in</h2>
<ul>
<li>
<Link href="/using-your-own-ui/sign-in/email-password">Email + Password</Link>
</li>
<li>
<Link href="/using-your-own-ui/sign-in/magic-auth">Magic Auth</Link>
</li>
<li>
<Link href="/using-your-own-ui/sign-in/github-oauth">GitHub OAuth</Link>
</li>
<li>
<Link href="/using-your-own-ui/sign-in/google-oauth">Google OAuth</Link>
</li>
<li>
<Link href="/using-your-own-ui/sign-in/microsoft-oauth">Microsoft OAuth</Link>
</li>
<li>
<Link href="/using-your-own-ui/sign-in/sso">Single Sign-On</Link>
</li>
</ul>
<h2>Other</h2>
<ul>
<li>
<Link href="/using-your-own-ui/mfa">Multi-Factor Auth</Link>
</li>
<li>
<Link href="/using-your-own-ui/verify-email">Verify email</Link>
</li>
<li>
<Link href="/using-your-own-ui/reset-password">Reset password</Link>
</li>
<li>
<Link href="/using-your-own-ui/users-table">Users table</Link>
</li>
<li>
<Link href="/using-your-own-ui/update-user">Update user</Link>
</li>
</ul>
</main>
);
}
================================================
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 `<form onSubmit>`
// - 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 (
<main key="email">
<h1>Reset password</h1>
<form action={sendResetAction}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
autoCapitalize="off"
autoComplete="username"
autoFocus
required
/>
</div>
<button type="submit">Send reset instructions</button>
</form>
<pre>{JSON.stringify(sendResetState, null, 2)}</pre>
</main>
);
}
return (
<main key="code">
<h1>Reset password</h1>
<form action={resetPasswordAction}>
<div>
<label htmlFor="newPassword">New Password</label>
<input
type="password"
name="newPassword"
id="newPassword"
autoCapitalize="off"
autoComplete="new-password"
autoFocus
required
/>
</div>
<input type="hidden" name="token" value={token} />
{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
<input
type="text"
name="email"
value={email}
autoComplete="username"
style={{ display: 'none' }}
/>
)}
<button type="submit">Continue</button>
</form>
<pre>{JSON.stringify(resetPasswordState, null, 2)}</pre>
</main>
);
}
================================================
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 `<form onSubmit>`
// - make an API call to your backend (e.g using `fetch`)
const [signInState, signInAction] = useFormState(signIn, { error: null });
return (
<main>
<h1>Sign-in</h1>
<h2>Email + Password</h2>
<form action={signInAction}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
autoCapitalize="off"
autoComplete="username"
autoFocus
required
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="password"
name="password"
id="password"
autoCapitalize="off"
autoComplete="current-password"
required
/>
</div>
<button type="submit">Sign-in</button>
</form>
<pre>{JSON.stringify(signInState, null, 2)}</pre>
</main>
);
}
================================================
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 (
<main>
<h1>Sign-in</h1>
<h2>GitHub OAuth</h2>
<a href={githubOAuthUrl}>Continue with GitHub</a>
<pre>{JSON.stringify(result, null, 2)}</pre>
</main>
);
}
================================================
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 (
<main>
<h1>Sign-in</h1>
<h2>Google OAuth</h2>
<a href={googleOAuthUrl}>Continue with Google</a>
<pre>{JSON.stringify(result, null, 2)}</pre>
</main>
);
}
================================================
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 `<form onSubmit>`
// - 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 (
<main key="email">
<h1>Sign-in</h1>
<h2>Magic Auth</h2>
<form action={sendCodeAction}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
autoCapitalize="off"
autoComplete="username"
autoFocus
required
onChange={(event) => setEmail(event.target.value)}
/>
</div>
<button type="submit">Send code</button>
</form>
<pre>{JSON.stringify(sendCodeState, null, 2)}</pre>
</main>
);
}
return (
<main key="code">
<h1>Sign-in</h1>
<h2>Magic Auth</h2>
<form action={signInAction}>
<div>
<label htmlFor="code">Enter code from the email</label>
<input
type="text"
name="code"
id="code"
inputMode="numeric"
autoComplete="one-time-code"
pattern="^\d{6}$"
autoFocus
required
/>
</div>
{/* we need the email to authenticate with the code */}
<input type="hidden" name="email" value={email} />
<button type="submit">Sign-in</button>
</form>
<pre>{JSON.stringify(signInState, null, 2)}</pre>
</main>
);
}
================================================
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 (
<main>
<h1>Sign-in</h1>
<h2>Microsoft OAuth</h2>
<a href={microsoftOAuthUrl}>Continue with Microsoft</a>
<pre>{JSON.stringify(result, null, 2)}</pre>
</main>
);
}
================================================
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 (
<main>
<h1>Sign-in</h1>
<h2>Single Sign-On</h2>
<a href={ssoUrl}>Continue with SSO</a>
<pre>{JSON.stringify(result, null, 2)}</pre>
</main>
);
}
================================================
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 `<form onSubmit>`
// - make an API call to your backend (e.g using `fetch`)
const [signUpState, signUpAction] = useFormState(signUp, { error: null });
return (
<main>
<h1>Sign-up</h1>
<h2>Email + Password</h2>
<form action={signUpAction}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
autoCapitalize="off"
autoComplete="username"
autoFocus
required
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="password"
name="password"
id="password"
autoCapitalize="off"
autoComplete="new-password"
required
/>
</div>
<div>
<label htmlFor="firstName">First Name</label>
<input type="text" name="firstName" id="firstName" autoComplete="given-name" />
</div>
<div>
<label htmlFor="lastName">Last Name</label>
<input type="text" name="lastName" id="lastName" autoComplete="family-name" />
</div>
<button type="submit">Sign-up</button>
</form>
<pre>{JSON.stringify(signUpState, null, 2)}</pre>
</main>
);
}
================================================
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 `<form onSubmit>`
// - make an API call to your backend (e.g using `fetch`)
const [signUpState, signUpAction] = useFormState(signUp, { error: null });
return (
<main>
<h1>Sign-up</h1>
<h2>Magic Auth</h2>
<form action={signUpAction}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
autoCapitalize="off"
autoComplete="username"
autoFocus
required
/>
</div>
<div>
<label htmlFor="firstName">First Name</label>
<input type="text" name="firstName" id="firstName" autoComplete="given-name" />
</div>
<div>
<label htmlFor="lastName">Last Name</label>
<input type="text" name="lastName" id="lastName" autoComplete="family-name" />
</div>
<button type="submit">Sign-up</button>
</form>
<pre>{JSON.stringify(signUpState, null, 2)}</pre>
</main>
);
}
================================================
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 `<form onSubmit>`
// - 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 (
<main key="email">
<h1>Update user</h1>
<form action={getUserAction}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
autoCapitalize="off"
autoComplete="username"
autoFocus
required
/>
</div>
<button type="submit">Continue</button>
</form>
<pre>{JSON.stringify(getUserState, null, 2)}</pre>
</main>
);
}
return (
<main key="code">
<h1>Update user</h1>
<form action={updateUserAction}>
<div>
<label htmlFor="email">Email</label>
<input type="text" name="email" id="email" readOnly value={getUserState.user.email} />
</div>
<div>
<label htmlFor="firstName">First Name</label>
<input
type="firstName"
name="firstName"
id="firstName"
autoComplete="given-name"
defaultValue={getUserState.user.firstName ?? ''}
autoFocus
/>
</div>
<div>
<label htmlFor="lastName">Last Name</label>
<input
type="lastName"
name="lastName"
id="lastName"
autoComplete="family-name"
defaultValue={getUserState.user.lastName ?? ''}
/>
</div>
<input type="hidden" name="userId" value={getUserState.user.id} />
<button type="submit">Update user details</button>
</form>
<pre>{JSON.stringify(updateUserState, null, 2)}</pre>
</main>
);
}
================================================
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<Response> {
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<Response> {
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 (
<main>
<h1>Users</h1>
<table>
<thead>
<tr>
<th style={{ width: '40%' }}>Email</th>
<th>Name</th>
<th style={{ width: '10%', textAlign: 'center' }}>Verified</th>
<th style={{ width: '10%', textAlign: 'right' }} />
</tr>
</thead>
<tbody>
{Array.from({ length: 3 }, (_, i) => (
<tr key={i}>
<td>…</td>
<td>…</td>
<td style={{ textAlign: 'center' }}>…</td>
<td>
<button style={{ visibility: 'hidden' }}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
<nav> </nav>
</main>
);
}
================================================
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 (
<main>
<h1>Users</h1>
<table>
<thead>
<tr>
<th style={{ width: '40%' }}>Email</th>
<th>Name</th>
<th style={{ width: '10%', textAlign: 'center' }}>Verified</th>
<th style={{ width: '10%', textAlign: 'right' }} />
</tr>
</thead>
<tbody>
{users.data.map((user) => (
<tr key={user.id}>
<td title={user.id}>{user.email}</td>
<td>{formatName(user)}</td>
<td style={{ textAlign: 'center' }}>{user.emailVerified ? 'Yes' : 'No'}</td>
<td style={{ textAlign: 'right' }}>
<form action={deleteUser}>
<input type="hidden" name="userId" value={user.id} />
<button type="submit">Delete</button>
</form>
</td>
</tr>
))}
</tbody>
</table>
<nav>
{before ? <Link href={`?before=${before}`}>Previous</Link> : 'Previous'}
{after ? <Link href={`?after=${after}`}>Next</Link> : 'Next'}
</nav>
</main>
);
}
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 `<form onSubmit>`
// - 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 (
<main key="email">
<h1>Verify email</h1>
<form action={sendCodeAction}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
autoCapitalize="off"
autoComplete="username"
autoFocus
required
/>
</div>
<button type="submit">Send code</button>
</form>
<pre>{JSON.stringify(sendCodeState, null, 2)}</pre>
</main>
);
}
return (
<main key="code">
<h1>Verify email</h1>
<form action={verifyEmailAction}>
<div>
<label htmlFor="code">Enter code from the email</label>
<input
type="text"
name="code"
id="code"
inputMode="numeric"
autoComplete="one-time-code"
pattern="^\d{6}$"
autoFocus
required
/>
</div>
<input type="hidden" name="userId" value={sendCodeState.user.id} />
<button type="submit">Continue</button>
</form>
<pre>{JSON.stringify(verifyEmailState, null, 2)}</pre>
</main>
);
}
================================================
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"]
}
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
SYMBOL INDEX (53 symbols across 39 files)
FILE: src/app/back-link.tsx
function BackLink (line 6) | function BackLink() {
FILE: src/app/layout.tsx
function RootLayout (line 14) | function RootLayout({ children }: { children: React.ReactNode }) {
FILE: src/app/page.tsx
function Home (line 3) | function Home() {
FILE: src/app/using-hosted-authkit/basic/callback/route.ts
function GET (line 16) | async function GET(request: Request) {
FILE: src/app/using-hosted-authkit/basic/page.tsx
function Basic (line 14) | function Basic({
FILE: src/app/using-hosted-authkit/page.tsx
function UsingHostedAuthKit (line 3) | function UsingHostedAuthKit() {
FILE: src/app/using-hosted-authkit/with-nextjs/callback/route.ts
constant GET (line 3) | const GET = handleAuth({ returnPathname: '/using-hosted-authkit/with-nex...
FILE: src/app/using-hosted-authkit/with-nextjs/page.tsx
function WithNextjs (line 3) | async function WithNextjs() {
FILE: src/app/using-hosted-authkit/with-session/auth.ts
function getJwtSecretKey (line 6) | function getJwtSecretKey() {
function verifyJwtToken (line 16) | async function verifyJwtToken(token: string) {
function getUser (line 26) | async function getUser(): Promise<{
function signOut (line 46) | async function signOut() {
FILE: src/app/using-hosted-authkit/with-session/callback/route.ts
function GET (line 14) | async function GET(request: Request) {
FILE: src/app/using-hosted-authkit/with-session/page.tsx
function WithSession (line 15) | async function WithSession() {
FILE: src/app/using-your-own-ui/mfa/mfa.ts
function signIn (line 17) | async function signIn(prevState: any, formData: FormData): Promise<SignI...
function verifyTotp (line 59) | async function verifyTotp(prevState: any, formData: FormData) {
type UnpackPromise (line 75) | type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type AuthenticateResponse (line 76) | type AuthenticateResponse = UnpackPromise<
type EnrollResponse (line 79) | type EnrollResponse = UnpackPromise<ReturnType<typeof workos.userManagem...
type SignInResponse (line 80) | type SignInResponse =
FILE: src/app/using-your-own-ui/mfa/page.tsx
function Mfa (line 7) | function Mfa() {
FILE: src/app/using-your-own-ui/page.tsx
function UsingYourOwnUi (line 3) | function UsingYourOwnUi() {
FILE: src/app/using-your-own-ui/reset-password/page.tsx
function ResetPassword (line 6) | function ResetPassword({
FILE: src/app/using-your-own-ui/reset-password/reset-password.ts
function sendReset (line 17) | async function sendReset(prevState: any, formData: FormData) {
function resetPassword (line 29) | async function resetPassword(prevState: any, formData: FormData) {
FILE: src/app/using-your-own-ui/sign-in/email-password/email-password.ts
function signIn (line 17) | async function signIn(prevState: any, formData: FormData) {
FILE: src/app/using-your-own-ui/sign-in/email-password/page.tsx
function SignInWithEmailPassword (line 6) | function SignInWithEmailPassword() {
FILE: src/app/using-your-own-ui/sign-in/github-oauth/callback/route.ts
function GET (line 16) | async function GET(request: Request) {
FILE: src/app/using-your-own-ui/sign-in/github-oauth/page.tsx
function SignInWithGitHubOAuth (line 14) | function SignInWithGitHubOAuth({
FILE: src/app/using-your-own-ui/sign-in/google-oauth/callback/route.ts
function GET (line 16) | async function GET(request: Request) {
FILE: src/app/using-your-own-ui/sign-in/google-oauth/page.tsx
function SignInWithGoogleOAuth (line 14) | function SignInWithGoogleOAuth({
FILE: src/app/using-your-own-ui/sign-in/magic-auth/magic-auth.ts
function sendCode (line 17) | async function sendCode(prevState: any, formData: FormData) {
function signIn (line 27) | async function signIn(prevState: any, formData: FormData) {
FILE: src/app/using-your-own-ui/sign-in/magic-auth/page.tsx
function SignInWithMagicAuth (line 7) | function SignInWithMagicAuth() {
FILE: src/app/using-your-own-ui/sign-in/microsoft-oauth/callback/route.ts
function GET (line 16) | async function GET(request: Request) {
FILE: src/app/using-your-own-ui/sign-in/microsoft-oauth/page.tsx
function SignInWithMicrosoftOAuth (line 14) | function SignInWithMicrosoftOAuth({
FILE: src/app/using-your-own-ui/sign-in/sso/callback/route.ts
function GET (line 16) | async function GET(request: Request) {
FILE: src/app/using-your-own-ui/sign-in/sso/page.tsx
function SignInWithSSO (line 14) | function SignInWithSSO({
FILE: src/app/using-your-own-ui/sign-up/email-password/email-password.ts
function signUp (line 17) | async function signUp(prevState: any, formData: FormData) {
FILE: src/app/using-your-own-ui/sign-up/email-password/page.tsx
function SignUpWithEmailPassword (line 6) | function SignUpWithEmailPassword() {
FILE: src/app/using-your-own-ui/sign-up/magic-auth/magic-auth.ts
function signUp (line 17) | async function signUp(prevState: any, formData: FormData) {
FILE: src/app/using-your-own-ui/sign-up/magic-auth/page.tsx
function SignUpWithMagicAuth (line 6) | function SignUpWithMagicAuth() {
FILE: src/app/using-your-own-ui/update-user/page.tsx
function UpdateUser (line 6) | function UpdateUser() {
FILE: src/app/using-your-own-ui/update-user/update-user.ts
function getUser (line 19) | async function getUser(prevState: any, formData: FormData): Promise<Resp...
function updateUser (line 29) | async function updateUser(prevState: any, formData: FormData): Promise<R...
type Response (line 43) | type Response = { user: User } | { error: any };
FILE: src/app/using-your-own-ui/users-table/loading.tsx
function Loading (line 1) | function Loading() {
FILE: src/app/using-your-own-ui/users-table/page.tsx
function UsersTable (line 17) | async function UsersTable({
function formatName (line 63) | function formatName(user: User) {
FILE: src/app/using-your-own-ui/users-table/users-table.ts
function deleteUser (line 18) | async function deleteUser(formData: FormData) {
FILE: src/app/using-your-own-ui/verify-email/page.tsx
function VerifyEmail (line 6) | function VerifyEmail() {
FILE: src/app/using-your-own-ui/verify-email/verify-email.ts
function sendCode (line 18) | async function sendCode(prevState: any, formData: FormData) {
function verifyEmail (line 28) | async function verifyEmail(prevState: any, formData: FormData) {
Condensed preview — 55 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (82K chars).
[
{
"path": ".eslintrc.json",
"chars": 40,
"preview": "{\n \"extends\": \"next/core-web-vitals\"\n}\n"
},
{
"path": ".github/workflows/coana-analysis.yml",
"chars": 751,
"preview": "name: Coana Vulnerability Analysis\n\non:\n schedule:\n - cron: \"0 3 * * *\" # every day at 3 AM\n workflow_dispatch:\n "
},
{
"path": ".github/workflows/coana-guardrail.yml",
"chars": 2379,
"preview": "name: Coana Guardrail\n\non: pull_request\n\njobs:\n guardrail:\n runs-on: ubuntu-latest\n\n steps:\n - name: Checkou"
},
{
"path": ".gitignore",
"chars": 391,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": ".prettierrc",
"chars": 47,
"preview": "{\n \"printWidth\": 100,\n \"singleQuote\": true\n}\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5220,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2023 WorkOS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "README.md",
"chars": 2759,
"preview": "<p align=\"center\">\n <img src=\"https://github.com/workos/authkit/assets/896475/9fa7a91e-f5a8-4922-96fb-20a7b478d075\" w"
},
{
"path": "context7.json",
"chars": 94,
"preview": "{\n \"url\": \"https://context7.com/workos/authkit\",\n \"public_key\": \"pk_q7NnKuFFXMWA7WnmjMHQU\"\n}"
},
{
"path": "next.config.js",
"chars": 92,
"preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {}\n\nmodule.exports = nextConfig\n"
},
{
"path": "package.json",
"chars": 566,
"preview": "{\n \"name\": \"authkit\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev\",\n \"build\": \"next"
},
{
"path": "src/app/back-link.tsx",
"chars": 385,
"preview": "'use client';\n\nimport Link from 'next/link';\nimport { usePathname } from 'next/navigation';\n\nexport default function Bac"
},
{
"path": "src/app/globals.css",
"chars": 2633,
"preview": "::selection {\n background-color: #003eff3e;\n}\n\nbody {\n margin: 0;\n font-size: 20px;\n}\n\na {\n color: black;\n text-dec"
},
{
"path": "src/app/layout.tsx",
"chars": 531,
"preview": "import type { Metadata } from 'next';\nimport { Inter } from 'next/font/google';\n\nimport './globals.css';\nimport BackLink"
},
{
"path": "src/app/page.tsx",
"chars": 346,
"preview": "import Link from 'next/link';\n\nexport default function Home() {\n return (\n <main>\n <h1>Examples</h1>\n <ul>"
},
{
"path": "src/app/using-hosted-authkit/README.md",
"chars": 3221,
"preview": "<p align=\"center\">\n <img src=\"https://github.com/workos/authkit/assets/896475/9fa7a91e-f5a8-4922-96fb-20a7b478d075\" w"
},
{
"path": "src/app/using-hosted-authkit/basic/callback/route.ts",
"chars": 1120,
"preview": "import { WorkOS } from '@workos-inc/node';\nimport { redirect } from 'next/navigation';\n\n// This is a Next.js Route Handl"
},
{
"path": "src/app/using-hosted-authkit/basic/page.tsx",
"chars": 1193,
"preview": "import { WorkOS } from '@workos-inc/node';\n\n// This example uses Next.js with React Server Components.\n// Because this p"
},
{
"path": "src/app/using-hosted-authkit/page.tsx",
"chars": 494,
"preview": "import Link from 'next/link';\n\nexport default function UsingHostedAuthKit() {\n return (\n <main>\n <h1>Using host"
},
{
"path": "src/app/using-hosted-authkit/with-nextjs/callback/route.ts",
"chars": 147,
"preview": "import { handleAuth } from '@workos-inc/authkit-nextjs';\n\nexport const GET = handleAuth({ returnPathname: '/using-hosted"
},
{
"path": "src/app/using-hosted-authkit/with-nextjs/page.tsx",
"chars": 888,
"preview": "import { getSignInUrl, getUser, signOut } from '@workos-inc/authkit-nextjs';\n\nexport default async function WithNextjs()"
},
{
"path": "src/app/using-hosted-authkit/with-session/auth.ts",
"chars": 1171,
"preview": "import { cookies } from 'next/headers';\nimport { redirect } from 'next/navigation';\nimport { jwtVerify } from 'jose';\nim"
},
{
"path": "src/app/using-hosted-authkit/with-session/callback/route.ts",
"chars": 1510,
"preview": "import { WorkOS } from '@workos-inc/node';\nimport { NextResponse } from 'next/server';\nimport { SignJWT } from 'jose';\ni"
},
{
"path": "src/app/using-hosted-authkit/with-session/page.tsx",
"chars": 1615,
"preview": "import { WorkOS } from '@workos-inc/node';\nimport { getUser, signOut } from './auth';\n\n// This example uses Next.js with"
},
{
"path": "src/app/using-your-own-ui/README.md",
"chars": 2763,
"preview": "<p align=\"center\">\n <img src=\"https://github.com/workos/authkit/assets/896475/9fa7a91e-f5a8-4922-96fb-20a7b478d075\" w"
},
{
"path": "src/app/using-your-own-ui/mfa/mfa.ts",
"chars": 3254,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/app/using-your-own-ui/mfa/page.tsx",
"chars": 2847,
"preview": "'use client';\n\nimport { useFormState } from 'react-dom';\nimport { signIn, verifyTotp } from './mfa';\nimport Image from '"
},
{
"path": "src/app/using-your-own-ui/page.tsx",
"chars": 1676,
"preview": "import Link from 'next/link';\n\nexport default function UsingYourOwnUi() {\n return (\n <main>\n <h1>Using your own"
},
{
"path": "src/app/using-your-own-ui/reset-password/page.tsx",
"chars": 2443,
"preview": "'use client';\n\nimport { useFormState } from 'react-dom';\nimport { sendReset, resetPassword } from './reset-password';\n\ne"
},
{
"path": "src/app/using-your-own-ui/reset-password/reset-password.ts",
"chars": 1273,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/app/using-your-own-ui/sign-in/email-password/email-password.ts",
"chars": 1135,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/app/using-your-own-ui/sign-in/email-password/page.tsx",
"chars": 1341,
"preview": "'use client';\n\nimport { useFormState } from 'react-dom';\nimport { signIn } from './email-password';\n\nexport default func"
},
{
"path": "src/app/using-your-own-ui/sign-in/github-oauth/callback/route.ts",
"chars": 1102,
"preview": "import { WorkOS } from '@workos-inc/node';\nimport { redirect } from 'next/navigation';\n\n// This is a Next.js Route Handl"
},
{
"path": "src/app/using-your-own-ui/sign-in/github-oauth/page.tsx",
"chars": 1219,
"preview": "import { WorkOS } from '@workos-inc/node';\n\n// This example uses Next.js with React Server Components.\n// Because this p"
},
{
"path": "src/app/using-your-own-ui/sign-in/google-oauth/callback/route.ts",
"chars": 1102,
"preview": "import { WorkOS } from '@workos-inc/node';\nimport { redirect } from 'next/navigation';\n\n// This is a Next.js Route Handl"
},
{
"path": "src/app/using-your-own-ui/sign-in/google-oauth/page.tsx",
"chars": 1219,
"preview": "import { WorkOS } from '@workos-inc/node';\n\n// This example uses Next.js with React Server Components.\n// Because this p"
},
{
"path": "src/app/using-your-own-ui/sign-in/magic-auth/magic-auth.ts",
"chars": 1400,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/app/using-your-own-ui/sign-in/magic-auth/page.tsx",
"chars": 2097,
"preview": "'use client';\n\nimport * as React from 'react';\nimport { useFormState } from 'react-dom';\nimport { sendCode, signIn } fro"
},
{
"path": "src/app/using-your-own-ui/sign-in/microsoft-oauth/callback/route.ts",
"chars": 1105,
"preview": "import { WorkOS } from '@workos-inc/node';\nimport { redirect } from 'next/navigation';\n\n// This is a Next.js Route Handl"
},
{
"path": "src/app/using-your-own-ui/sign-in/microsoft-oauth/page.tsx",
"chars": 1240,
"preview": "import { WorkOS } from '@workos-inc/node';\n\n// This example uses Next.js with React Server Components.\n// Because this p"
},
{
"path": "src/app/using-your-own-ui/sign-in/sso/callback/route.ts",
"chars": 1077,
"preview": "import { WorkOS } from '@workos-inc/node';\nimport { redirect } from 'next/navigation';\n\n// This is a Next.js Route Handl"
},
{
"path": "src/app/using-your-own-ui/sign-in/sso/page.tsx",
"chars": 1223,
"preview": "import { WorkOS } from '@workos-inc/node';\n\n// This example uses Next.js with React Server Components.\n// Because this p"
},
{
"path": "src/app/using-your-own-ui/sign-up/email-password/email-password.ts",
"chars": 1114,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/app/using-your-own-ui/sign-up/email-password/page.tsx",
"chars": 1686,
"preview": "'use client';\n\nimport { useFormState } from 'react-dom';\nimport { signUp } from './email-password';\n\nexport default func"
},
{
"path": "src/app/using-your-own-ui/sign-up/magic-auth/magic-auth.ts",
"chars": 1217,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/app/using-your-own-ui/sign-up/magic-auth/page.tsx",
"chars": 1383,
"preview": "'use client';\n\nimport { useFormState } from 'react-dom';\nimport { signUp } from './magic-auth';\n\nexport default function"
},
{
"path": "src/app/using-your-own-ui/update-user/page.tsx",
"chars": 2324,
"preview": "'use client';\n\nimport { useFormState } from 'react-dom';\nimport { getUser, updateUser } from './update-user';\n\nexport de"
},
{
"path": "src/app/using-your-own-ui/update-user/update-user.ts",
"chars": 1476,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/app/using-your-own-ui/users-table/loading.tsx",
"chars": 792,
"preview": "export default function Loading() {\n return (\n <main>\n <h1>Users</h1>\n\n <table>\n <thead>\n "
},
{
"path": "src/app/using-your-own-ui/users-table/page.tsx",
"chars": 2248,
"preview": "import { WorkOS } from '@workos-inc/node';\nimport type { User } from '@workos-inc/node';\nimport Link from 'next/link';\ni"
},
{
"path": "src/app/using-your-own-ui/users-table/users-table.ts",
"chars": 822,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/app/using-your-own-ui/verify-email/page.tsx",
"chars": 1883,
"preview": "'use client';\n\nimport { useFormState } from 'react-dom';\nimport { sendCode, verifyEmail } from './verify-email';\n\nexport"
},
{
"path": "src/app/using-your-own-ui/verify-email/verify-email.ts",
"chars": 1351,
"preview": "'use server';\n\n// These are Next.js server actions.\n//\n// If your application is a single page app (SPA) with a separate"
},
{
"path": "src/middleware.ts",
"chars": 312,
"preview": "// This file is only used in conjunction with the authkit-nextjs library\nimport { authkitMiddleware } from '@workos-inc/"
},
{
"path": "tsconfig.json",
"chars": 599,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"sk"
}
]
About this extraction
This page contains the full source code of the workos/authkit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 55 files (73.6 KB), approximately 20.4k tokens, and a symbol index with 53 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.