Repository: sst/openauth
Branch: master
Commit: 98dc59625e65
Files: 237
Total size: 577.6 KB
Directory structure:
gitextract_6_z9n4fm/
├── .changeset/
│ ├── README.md
│ ├── commit.cjs
│ ├── config.json
│ ├── popular-geese-reply.md
│ ├── stupid-boats-play.md
│ └── ten-pans-invent.md
├── .github/
│ ├── CODE_OF_CONDUCT
│ └── workflows/
│ ├── docs.yml
│ ├── format.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .prettierrc
├── CNAME
├── LICENSE
├── README.md
├── bun.lockb
├── bunfig.toml
├── examples/
│ ├── .gitignore
│ ├── README.md
│ ├── client/
│ │ ├── astro/
│ │ │ ├── .gitignore
│ │ │ ├── .vscode/
│ │ │ │ ├── extensions.json
│ │ │ │ └── launch.json
│ │ │ ├── README.md
│ │ │ ├── astro.config.mjs
│ │ │ ├── package.json
│ │ │ ├── src/
│ │ │ │ ├── auth.ts
│ │ │ │ ├── components/
│ │ │ │ │ └── Welcome.astro
│ │ │ │ ├── env.d.ts
│ │ │ │ ├── layouts/
│ │ │ │ │ └── Layout.astro
│ │ │ │ ├── middleware.ts
│ │ │ │ └── pages/
│ │ │ │ ├── callback.ts
│ │ │ │ └── index.astro
│ │ │ └── tsconfig.json
│ │ ├── cloudflare-api/
│ │ │ ├── api.ts
│ │ │ └── package.json
│ │ ├── jwt-api/
│ │ │ ├── CHANGELOG.md
│ │ │ ├── README.md
│ │ │ ├── index.ts
│ │ │ └── package.json
│ │ ├── lambda-api/
│ │ │ ├── api.ts
│ │ │ └── package.json
│ │ ├── nextjs/
│ │ │ ├── .gitignore
│ │ │ ├── CHANGELOG.md
│ │ │ ├── README.md
│ │ │ ├── app/
│ │ │ │ ├── actions.ts
│ │ │ │ ├── api/
│ │ │ │ │ └── callback/
│ │ │ │ │ └── route.ts
│ │ │ │ ├── auth.ts
│ │ │ │ ├── globals.css
│ │ │ │ ├── layout.tsx
│ │ │ │ ├── page.module.css
│ │ │ │ └── page.tsx
│ │ │ ├── next.config.ts
│ │ │ ├── package.json
│ │ │ └── tsconfig.json
│ │ ├── react/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── index.html
│ │ │ ├── package.json
│ │ │ ├── src/
│ │ │ │ ├── App.tsx
│ │ │ │ ├── AuthContext.tsx
│ │ │ │ ├── main.tsx
│ │ │ │ └── vite-env.d.ts
│ │ │ ├── tsconfig.app.json
│ │ │ ├── tsconfig.json
│ │ │ ├── tsconfig.node.json
│ │ │ └── vite.config.ts
│ │ └── sveltekit/
│ │ ├── .npmrc
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.d.ts
│ │ │ ├── app.html
│ │ │ ├── hooks.server.ts
│ │ │ ├── lib/
│ │ │ │ └── auth.server.ts
│ │ │ └── routes/
│ │ │ ├── +page.server.ts
│ │ │ ├── +page.svelte
│ │ │ └── callback/
│ │ │ └── +server.ts
│ │ ├── svelte.config.js
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── issuer/
│ │ ├── bun/
│ │ │ ├── .gitignore
│ │ │ ├── issuer.ts
│ │ │ └── package.json
│ │ ├── cloudflare/
│ │ │ ├── issuer.ts
│ │ │ ├── package.json
│ │ │ ├── sst-env.d.ts
│ │ │ └── sst.config.ts
│ │ ├── custom-frontend/
│ │ │ ├── auth/
│ │ │ │ ├── issuer.ts
│ │ │ │ └── package.json
│ │ │ ├── frontend/
│ │ │ │ ├── frontend.tsx
│ │ │ │ └── package.json
│ │ │ └── package.json
│ │ ├── lambda/
│ │ │ ├── issuer.ts
│ │ │ ├── package.json
│ │ │ ├── sst-env.d.ts
│ │ │ └── sst.config.ts
│ │ └── node/
│ │ ├── .gitignore
│ │ ├── authorizer.ts
│ │ └── package.json
│ ├── quickstart/
│ │ ├── sst/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── app/
│ │ │ │ ├── actions.ts
│ │ │ │ ├── api/
│ │ │ │ │ └── callback/
│ │ │ │ │ └── route.ts
│ │ │ │ ├── auth.ts
│ │ │ │ ├── globals.css
│ │ │ │ ├── layout.tsx
│ │ │ │ ├── page.module.css
│ │ │ │ └── page.tsx
│ │ │ ├── auth/
│ │ │ │ ├── index.ts
│ │ │ │ └── subjects.ts
│ │ │ ├── next.config.ts
│ │ │ ├── package.json
│ │ │ ├── sst-env.d.ts
│ │ │ ├── sst.config.ts
│ │ │ └── tsconfig.json
│ │ └── standalone/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── actions.ts
│ │ │ ├── api/
│ │ │ │ └── callback/
│ │ │ │ └── route.ts
│ │ │ ├── auth.ts
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ ├── page.module.css
│ │ │ └── page.tsx
│ │ ├── auth/
│ │ │ ├── index.ts
│ │ │ └── subjects.ts
│ │ ├── bun.lockb
│ │ ├── next.config.ts
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── subjects.ts
│ └── tsconfig.json
├── package.json
├── packages/
│ └── openauth/
│ ├── CHANGELOG.md
│ ├── bunfig.toml
│ ├── package.json
│ ├── script/
│ │ └── build.ts
│ ├── src/
│ │ ├── client.ts
│ │ ├── css.d.ts
│ │ ├── error.ts
│ │ ├── index.ts
│ │ ├── issuer.ts
│ │ ├── jwt.ts
│ │ ├── keys.ts
│ │ ├── pkce.ts
│ │ ├── provider/
│ │ │ ├── apple.ts
│ │ │ ├── arctic.ts
│ │ │ ├── code.ts
│ │ │ ├── cognito.ts
│ │ │ ├── discord.ts
│ │ │ ├── facebook.ts
│ │ │ ├── github.ts
│ │ │ ├── google.ts
│ │ │ ├── index.ts
│ │ │ ├── jumpcloud.ts
│ │ │ ├── keycloak.ts
│ │ │ ├── linkedin.ts
│ │ │ ├── microsoft.ts
│ │ │ ├── oauth2.ts
│ │ │ ├── oidc.ts
│ │ │ ├── password.ts
│ │ │ ├── provider.ts
│ │ │ ├── slack.ts
│ │ │ ├── spotify.ts
│ │ │ ├── twitch.ts
│ │ │ ├── x.ts
│ │ │ └── yahoo.ts
│ │ ├── random.ts
│ │ ├── storage/
│ │ │ ├── aws.ts
│ │ │ ├── cloudflare.ts
│ │ │ ├── dynamo.ts
│ │ │ ├── memory.ts
│ │ │ └── storage.ts
│ │ ├── subject.ts
│ │ ├── ui/
│ │ │ ├── base.tsx
│ │ │ ├── code.tsx
│ │ │ ├── form.tsx
│ │ │ ├── icon.tsx
│ │ │ ├── password.tsx
│ │ │ ├── select.tsx
│ │ │ ├── theme.ts
│ │ │ └── ui.css
│ │ └── util.ts
│ ├── test/
│ │ ├── client.test.ts
│ │ ├── issuer.test.ts
│ │ ├── scrap.test.ts
│ │ ├── storage.test.ts
│ │ └── util.test.ts
│ └── tsconfig.json
├── scripts/
│ └── format
└── www/
├── .gitignore
├── .vscode/
│ ├── extensions.json
│ └── launch.json
├── README.md
├── astro.config.mjs
├── bun.lockb
├── config.ts
├── generate.ts
├── package.json
├── src/
│ ├── components/
│ │ ├── Hero.astro
│ │ └── Lander.astro
│ ├── content/
│ │ ├── config.ts
│ │ └── docs/
│ │ ├── docs/
│ │ │ ├── client.mdx
│ │ │ ├── index.mdx
│ │ │ ├── issuer.mdx
│ │ │ ├── provider/
│ │ │ │ ├── apple.mdx
│ │ │ │ ├── code.mdx
│ │ │ │ ├── cognito.mdx
│ │ │ │ ├── discord.mdx
│ │ │ │ ├── facebook.mdx
│ │ │ │ ├── github.mdx
│ │ │ │ ├── google.mdx
│ │ │ │ ├── jumpcloud.mdx
│ │ │ │ ├── keycloak.mdx
│ │ │ │ ├── microsoft.mdx
│ │ │ │ ├── oauth2.mdx
│ │ │ │ ├── oidc.mdx
│ │ │ │ ├── password.mdx
│ │ │ │ ├── slack.mdx
│ │ │ │ ├── spotify.mdx
│ │ │ │ ├── twitch.mdx
│ │ │ │ ├── x.mdx
│ │ │ │ └── yahoo.mdx
│ │ │ ├── start/
│ │ │ │ ├── sst.mdx
│ │ │ │ └── standalone.mdx
│ │ │ ├── storage/
│ │ │ │ ├── cloudflare.mdx
│ │ │ │ ├── dynamo.mdx
│ │ │ │ └── memory.mdx
│ │ │ ├── subject.mdx
│ │ │ └── ui/
│ │ │ ├── code.mdx
│ │ │ ├── password.mdx
│ │ │ ├── select.mdx
│ │ │ └── theme.mdx
│ │ └── index.mdx
│ ├── custom.css
│ ├── env.d.ts
│ └── styles/
│ └── lander.css
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .changeset/README.md
================================================
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
================================================
FILE: .changeset/commit.cjs
================================================
/** @type {import('@changesets/types').CommitFunctions["getAddMessage"]} */
module.exports.getAddMessage = async (changeset) => {
return changeset.summary;
};
================================================
FILE: .changeset/config.json
================================================
{
"$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": "./commit.cjs",
"fixed": [["@openauthjs/openauth"]],
"linked": [],
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": ["@openauthjs/example-*"]
}
================================================
FILE: .changeset/popular-geese-reply.md
================================================
---
"@openauthjs/openauth": patch
---
update google icon to comply with branding guidelines
================================================
FILE: .changeset/stupid-boats-play.md
================================================
---
"@openauthjs/openauth": patch
---
allow auth style autodetection
================================================
FILE: .changeset/ten-pans-invent.md
================================================
---
"@openauthjs/openauth": patch
---
add linkedin adapter
================================================
FILE: .github/CODE_OF_CONDUCT
================================================
# Code of Conduct
I don't typically set up a code of conduct for our projects but given this one is security related it will draw a very specific set of problems I want to avoid. There's only two rules
1. Reporting security issues
If you find a security issue please report them to me directly on [X](https://twitter.com/thdxr) or [Bluesky](https://bsky.app/). Do not open a public issue or post publicly in case the issue can be exploited. Feel free to give us a window of time to respond before disclosing it publicly - that seems fair.
2. Reporting "security" issues
A lot of things that seem to fall in that first category are not really security problems, just tradeoffs that were made in the design of OpenAuth. Security products attract a lot of binary opinions like "never use X". We reject this type of thinking entirely - security is a spectrum of usability and infinitely optimizing for "security" does not yield a good product.
All discussions around the tradeoffs that were made must consider this - if you disagree with a decision you MUST articulate why the decision was probably made before you argue against it. Eg. "X seem to be used because of benefit [a] and their downside [b] is mitigated by [c] BUT I do not think this is enough because of [d]"
We do not tolerate wasting the maintainers time and forcing them to articulate this nuance. If something is not clear of course you can ask for clarification.
================================================
FILE: .github/workflows/docs.yml
================================================
name: docs
on:
# Trigger the workflow every time you push to the `main` branch
# Using a different branch name? Replace `main` with your branch’s name
push:
branches: [master]
# Allows you to run this workflow manually from the Actions tab on GitHub.
workflow_dispatch:
# Allow this job to clone the repo and create a page deployment
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout your repository using git
uses: actions/checkout@v4
- name: Install, build, and upload your site
uses: withastro/action@v3
with:
path: www
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
with:
path: www
================================================
FILE: .github/workflows/format.yml
================================================
name: format
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
jobs:
format:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
- run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
./scripts/format
================================================
FILE: .github/workflows/release.yml
================================================
name: release
on:
push:
branches:
- master
permissions:
contents: write
pull-requests: write
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v2
- run: bun install
- id: changesets
uses: changesets/action@v1
with:
publish: bun run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .github/workflows/test.yml
================================================
name: test
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- run: bun install
- run: cd packages/openauth && bun run build
- run: cd packages/openauth && bun test
================================================
FILE: .gitignore
================================================
/node_modules
.sst
.env
dist
persist.json
.DS_Store
notes
.nvim.lua
.svelte-kit
================================================
FILE: .prettierrc
================================================
{
"semi": false,
}
================================================
FILE: CNAME
================================================
openauth.js.org
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 SST
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
================================================
---
[OpenAuth](https://openauth.js.org) is a standards-based auth provider for web apps, mobile apps, single pages apps, APIs, or 3rd party clients. It is currently in beta.
- **Universal**: You can deploy it as a standalone service or embed it into an existing application. It works with any framework or platform.
- **Self-hosted**: It runs entirely on your infrastructure and can be deployed on Node.js, Bun, AWS Lambda, or Cloudflare Workers.
- **Standards-based**: It implements the OAuth 2.0 spec and is based on web standards. So any OAuth client can use it.
- **Customizable**: It comes with prebuilt themeable UI that you can customize or opt out of.
## Quick Start
If you just want to get started as fast as possible you can jump straight into the [code examples](https://github.com/toolbeam/openauth/tree/master/examples) folder and copy paste away. There are also [SST components](https://sst.dev/docs/component/aws/auth) for deploying everything OpenAuth needs.
## Approach
While there are many open source solutions for auth, almost all of them are libraries that are meant to be embedded into a single application. Centralized auth servers typically are delivered as SaaS services - eg Auth0 or Clerk.
OpenAuth instead is a centralized auth server that runs on your own infrastructure and has been designed for ease of self hosting. It can be used to authenticate all of your applications - web apps, mobile apps, internal admin tools, etc.
It adheres mostly to OAuth 2.0 specifications - which means anything that can speak OAuth can use it to receive access and refresh tokens. When a client initiates an authorization flow, OpenAuth will hand off to one of the configured providers - this can be third party identity providers like Google, GitHub, etc or built in flows like email/password or pin code.
Because it follows these specifications it can even be used to issue credentials for third party applications - allowing you to implement "login with myapp" flows.
OpenAuth very intentionally does not attempt to solve user management. We've found that this is a very difficult problem given the wide range of databases and drivers that are used in the JS ecosystem. Additionally it's quite hard to build data abstractions that work for every use case. Instead, once a user has identified themselves OpenAuth will invoke a callback where you can implement your own user lookup/creation logic.
While OpenAuth tries to be mostly stateless, it does need to store a minimal amount of data (refresh tokens, password hashes, etc). However this has been reduced to a simple KV store with various implementations for zero overhead systems like Cloudflare KV and DynamoDB. You should never need to directly access any data that is stored in there.
There is also a themeable UI that you can use to get going without implementing any designs yourself. This is built on top of a lower level system so you can copy paste the default UI and tweak it or opt out entirely and implement your own.
Finally, OpenAuth is created by the maintainers of [SST](https://sst.dev) which is a tool to manage all the infrastructure for your app. It contains components for OpenAuth that make deploying it to AWS or Cloudflare as simple as it can get.
## Tutorial
We'll show how to deploy the auth server and then a sample app that uses it.
### Auth server
Start by importing the `issuer` function from the `@openauthjs/openauth` package.
```ts
import { issuer } from "@openauthjs/openauth"
```
OpenAuth is built on top of [Hono](https://github.com/honojs/hono) which is a minimal web framework that can run anywhere. The `issuer` function creates a Hono app with all of the auth server implemented that you can then deploy to AWS Lambda, Cloudflare Workers, or in a container running under Node.js or Bun.
The `issuer` function requires a few things:
```ts
const app = issuer({
providers: { ... },
storage,
subjects,
success: async (ctx, value) => { ... }
})
```
First we need to define some providers that are enabled - these are either third party identity providers like Google, GitHub, etc or built in flows like email/password or pin code. You can also implement your own. Let's try the GitHub provider.
```ts
import { GithubProvider } from "@openauthjs/openauth/provider/github"
const app = issuer({
providers: {
github: GithubProvider({
clientID: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
scopes: ["user:email"],
}),
},
...
})
```
Providers take some configuration - since this is a third party identity provider there is no UI to worry about and all it needs is a client ID, secret and some scopes. Let's add the password provider which is a bit more complicated.
```ts
import { PasswordProvider } from "@openauthjs/openauth/provider/password"
const app = issuer({
providers: {
github: ...,
password: PasswordProvider(...),
},
...
})
```
The password provider is quite complicated as username/password involve a lot of flows so there are a lot of callbacks to implement. However you can opt into the default UI which has all of this already implemented for you. The only thing you have to specify is how to send a code for forgot password/email verification. In this case we'll log the code but you would send this over email.
```ts
import { PasswordProvider } from "@openauthjs/openauth/provider/password"
import { PasswordUI } from "@openauthjs/openauth/ui/password"
const app = issuer({
providers: {
github: ...,
password: PasswordProvider(
PasswordUI({
sendCode: async (email, code) => {
console.log(email, code)
},
}),
),
},
...
})
```
Next up is the `subjects` field. Subjects are what the access token generated at the end of the auth flow will map to. Under the hood, the access token is a JWT that contains this data. You will likely just have a single subject to start but you can define additional ones for different types of users.
```ts
import { object, string } from "valibot"
const subjects = createSubjects({
user: object({
userID: string(),
// may want to add workspaceID here if doing a multi-tenant app
workspaceID: string(),
}),
})
```
Note we are using [valibot](https://github.com/fabian-hiller/valibot) to define the shape of the subject so it can be validated properly. You can use any validation library that is following the [standard-schema specification](https://github.com/standard-schema/standard-schema) - the next version of Zod will support this.
You typically will want to place subjects in its own file as it can be imported by all of your apps. You can pass it to the issuer in the `subjects` field.
```ts
import { subjects } from "./subjects.js"
const app = issuer({
providers: { ... },
subjects,
...
})
```
Next we'll implement the `success` callback which receives the payload when a user successfully completes a provider flow.
```ts
const app = issuer({
providers: { ... },
subjects,
async success(ctx, value) {
let userID
if (value.provider === "password") {
console.log(value.email)
userID = ... // lookup user or create them
}
if (value.provider === "github") {
console.log(value.tokenset.access)
userID = ... // lookup user or create them
}
return ctx.subject("user", {
userID,
'a workspace id'
})
}
})
```
Note all of this is typesafe - based on the configured providers you will receive different properties in the `value` object. Also the `subject` method will only accept properties. Note - most callbacks in OpenAuth can return a `Response` object. In this case if something goes wrong, you can return a `Response.redirect("...")` sending them to a different place or rendering an error.
Next we have the `storage` field which defines where things like refresh tokens and password hashes are stored. If on AWS we recommend DynamoDB, if on Cloudflare we recommend Cloudflare KV. We also have a MemoryStore used for testing.
```ts
import { MemoryStorage } from "@openauthjs/openauth/storage/memory"
const app = issuer({
providers: { ... },
subjects,
async success(ctx, value) { ... },
storage: MemoryStorage(),
})
```
And now we are ready to deploy! Here's how you do that depending on your infrastructure.
```ts
// Bun
export default app
// Cloudflare
export default app
// Lambda
import { handle } from "hono/aws-lambda"
export const handler = handle(app)
// Node.js
import { serve } from "@hono/node-server"
serve(app)
```
You now have a centralized auth server. Test it out by visiting `/.well-known/oauth-authorization-server` - you can see a live example [here](https://auth.terminal.shop/.well-known/oauth-authorization-server).
### Auth client
Since this is a standard OAuth server you can use any libraries for OAuth and it will work. OpenAuth does provide some light tooling for this although even a manual flow is pretty simple. You can create a client like this:
```ts
import { createClient } from "@openauthjs/openauth/client"
const client = createClient({
clientID: "my-client",
issuer: "https://auth.myserver.com", // url to the OpenAuth server
})
```
#### SSR Sites
If your frontend has a server component you can use the code flow. Redirect the user here
```ts
const { url } = await client.authorize(
,
"code"
)
```
You can make up a `client_id` that represents your app. This will initiate the auth flow and user will be redirected to the `redirect_uri` you provided with a query parameter `code` which you can exchange for an access token.
```ts
// the redirect_uri is the original redirect_uri you passed in and is used for verification
const tokens = await client.exchange(query.get("code"), redirect_uri)
console.log(tokens.access, tokens.refresh)
```
You likely want to store both the access token and refresh token in an HTTP only cookie so they are sent up with future requests. Then you can use the `client` to verify the tokens.
```ts
const verified = await client.verify(subjects, cookies.get("access_token")!, {
refresh: cookies.get("refresh_token") || undefined,
})
console.log(
verified.subject.type,
verified.subject.properties,
verified.refresh,
verified.access,
)
```
Passing in the refresh token is optional but if you do, this function will automatically refresh the access token if it has expired. It will return a new access token and refresh token which you should set back into the cookies.
#### SPA Sites, Mobile apps, etc
In cases where you do not have a server, you can use the `token` flow with `pkce` on the frontend.
```ts
const { challenge, url } = await client.authorize(, "code", { pkce: true })
localStorage.setItem("challenge", JSON.stringify(challenge))
location.href = url
```
When the auth flow is complete the user's browser will be redirected to the `redirect_uri` with a `code` query parameter. You can then exchange the code for access/refresh tokens.
```ts
const challenge = JSON.parse(localStorage.getItem("challenge"))
const exchanged = await client.exchange(
query.get("code"),
redirect_uri,
challenge.verifier,
)
if (exchanged.err) throw new Error("Invalid code")
localStorage.setItem("access_token", exchanged.tokens.access)
localStorage.setItem("refresh_token", exchanged.tokens.refresh)
```
Then when you make requests to your API you can include the access token in the `Authorization` header.
```ts
const accessToken = localStorage.getItem("access_token")
fetch("https://auth.example.com/api/user", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
```
And then you can verify the access token on the server.
```ts
const verified = await client.verify(subjects, accessToken)
console.log(verified.subject)
```
---
OpenAuth is created by the maintainers of [SST](https://sst.dev).
**Join our community** [Discord](https://sst.dev/discord) | [YouTube](https://www.youtube.com/c/sst-dev) | [X.com](https://x.com/SST_dev)
================================================
FILE: bunfig.toml
================================================
[install]
exact = true
================================================
FILE: examples/.gitignore
================================================
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
# Logs
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Caches
.cache
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Runtime data
pids
_.pid
_.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store
# sst
.sst
================================================
FILE: examples/README.md
================================================
# Examples
There are two sets of examples here, issuers and clients. Issuers are examples of setting up an OpenAuth server. The clients are examples of using OpenAuth in a client application and work with any of the issuer servers.
The fastest way to play around is to use the bun issuer. You can bring it up with:
```shell
$ bun run --hot ./issuer/bun/issuer.ts
```
You might have to install some workspace packages first, run this in the root:
```shell
$ bun install
$ cd packages/openauth
$ bun run build
```
This will bring it up on port 3000. Then try one of the clients - for example the astro one.
```
$ cd client/astro
$ bun dev
```
Now visit `http://localhost:4321` (the astro app) and experience the auth flow.
Or head over to `http://localhost:3000/password/authorize` to try the password flow directly.
================================================
FILE: examples/client/astro/.gitignore
================================================
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
.idea/
================================================
FILE: examples/client/astro/.vscode/extensions.json
================================================
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}
================================================
FILE: examples/client/astro/.vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}
================================================
FILE: examples/client/astro/README.md
================================================
# OpenAuth Astro Client
The files to note are
- `src/auth.ts` - creates the client that is used to interact with the auth server
- `src/middleware.ts` - middleware that runs to verify access tokens, refresh them if out of date, and redirect the user to the auth server if they are not logged in
- `src/pages/callback.ts` - the callback endpoint that receives the auth code and exchanges it for an access/refresh token
================================================
FILE: examples/client/astro/astro.config.mjs
================================================
// @ts-check
import { defineConfig } from "astro/config";
// https://astro.build/config
export default defineConfig({
output: "server",
server: {
host: "0.0.0.0",
},
});
================================================
FILE: examples/client/astro/package.json
================================================
{
"name": "@openauthjs/example-client-astro",
"type": "module",
"version": "0.0.0",
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@openauthjs/openauth": "workspace:*",
"astro": "5.0.2"
}
}
================================================
FILE: examples/client/astro/src/auth.ts
================================================
import { createClient } from "@openauthjs/openauth/client"
import type { APIContext } from "astro"
export { subjects } from "../../../subjects"
export const client = createClient({
clientID: "astro",
issuer: "http://localhost:3000",
})
export function setTokens(ctx: APIContext, access: string, refresh: string) {
ctx.cookies.set("refresh_token", refresh, {
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 34560000,
})
ctx.cookies.set("access_token", access, {
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 34560000,
})
}
================================================
FILE: examples/client/astro/src/components/Welcome.astro
================================================
---
import astroLogo from '../assets/astro.svg';
import background from '../assets/background.svg';
---
)
return new Response(jsx.toString(), {
headers: {
"Content-Type": "text/html",
},
})
}
}
const DISPLAY: Record = {
twitch: "Twitch",
google: "Google",
github: "GitHub",
apple: "Apple",
x: "X",
facebook: "Facebook",
microsoft: "Microsoft",
slack: "Slack",
}
const ICON: Record = {
code: (
),
password: (
),
twitch: (
),
google: ICON_GOOGLE,
github: ICON_GITHUB,
apple: (
),
x: (
),
microsoft: (
),
facebook: (
),
slack: (
),
}
================================================
FILE: packages/openauth/src/ui/theme.ts
================================================
/**
* Use one of the built-in themes.
*
* @example
*
* ```ts
* import { THEME_SST } from "@openauthjs/openauth/ui/theme"
*
* export default issuer({
* theme: THEME_SST,
* // ...
* })
* ```
*
* Or define your own.
*
* ```ts
* import type { Theme } from "@openauthjs/openauth/ui/theme"
*
* const MY_THEME: Theme = {
* title: "Acne",
* radius: "none",
* favicon: "https://www.example.com/favicon.svg",
* // ...
* }
*
* export default issuer({
* theme: MY_THEME,
* // ...
* })
* ```
*
* @packageDocumentation
*/
/**
* A type to define values for light and dark mode.
*
* @example
* ```ts
* {
* light: "#FFF",
* dark: "#000"
* }
* ```
*/
export interface ColorScheme {
/**
* The value for dark mode.
*/
dark: string
/**
* The value for light mode.
*/
light: string
}
/**
* A type to define your custom theme.
*/
export interface Theme {
/**
* The name of your app. Also used as the title of the page.
*
* @example
* ```ts
* {
* title: "Acne"
* }
* ```
*/
title?: string
/**
* A URL to the favicon of your app.
*
* @example
* ```ts
* {
* favicon: "https://www.example.com/favicon.svg"
* }
* ```
*/
favicon?: string
/**
* The border radius of the UI elements.
*
* @example
* ```ts
* {
* radius: "none"
* }
* ```
*/
radius?: "none" | "sm" | "md" | "lg" | "full"
/**
* The primary color of the theme.
*
* Takes a color or both light and dark colors.
*
* @example
* ```ts
* {
* primary: "#FF5E00"
* }
* ```
*/
primary: string | ColorScheme
/**
* The background color of the theme.
*
* Takes a color or both light and dark colors.
*
* @example
* ```ts
* {
* background: "#FFF"
* }
* ```
*/
background?: string | ColorScheme
/**
* A URL to the logo of your app.
*
* Takes a single image or both light and dark mode versions.
*
* @example
* ```ts
* {
* logo: "https://www.example.com/logo.svg"
* }
* ```
*/
logo?: string | ColorScheme
/**
* The font family and scale of the theme.
*/
font?: {
/**
* The font family of the theme.
*
* @example
* ```ts
* {
* font: {
* family: "Geist Mono, monospace"
* }
* }
* ```
*/
family?: string
/**
* The font scale of the theme. Can be used to increase or decrease the font sizes across
* the UI.
*
* @default "1"
* @example
* ```ts
* {
* font: {
* scale: "1.25"
* }
* }
* ```
*/
scale?: string
}
/**
* Custom CSS that's added to the page in a `
================================================
FILE: www/src/content/config.ts
================================================
import { defineCollection } from "astro:content"
import { docsSchema } from "@astrojs/starlight/schema"
export const collections = {
docs: defineCollection({ schema: docsSchema() }),
}
================================================
FILE: www/src/content/docs/docs/client.mdx
================================================
---
title: Client
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/client.ts
description: Reference doc for the OpenAuth client.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use the OpenAuth client kick off your OAuth flows, exchange tokens, refresh tokens,
and verify tokens.
First, create a client.
```ts title="client.ts"
import { createClient } from "@openauthjs/openauth/client"
const client = createClient({
clientID: "my-client",
issuer: "https://auth.myserver.com"
})
```
Kick off the OAuth flow by calling `authorize`.
```ts
const redirect_uri = "https://myserver.com/callback"
const { url } = await client.authorize(
redirect_uri,
"code"
)
```
When the user completes the flow, `exchange` the code for tokens.
```ts
const tokens = await client.exchange(query.get("code"), redirect_uri)
```
And `verify` the tokens.
```ts
const verified = await client.verify(subjects, tokens.access)
```
---
## Methods
### createClient
```ts
createClient(input)
```
#### Parameters
-
input [ClientInput](#clientinput)
Configure the client.
**Returns** [Client](#client)
Create an OpenAuth client.
## Client
An instance of the OpenAuth client contains the following methods.
### authorize
**Type** (redirectURI: string, response: “code” | “token”, opts?: [AuthorizeOptions](#authorizeoptions)) => Promise<[AuthorizeResult](#authorizeresult)>
Start the autorization flow. For example, in SSR sites.
```ts
const { url } = await client.authorize(, "code")
```
This takes a redirect URI and the type of flow you want to use. The redirect URI is the
location where the user will be redirected to after the flow is complete.
Supports both the _code_ and _token_ flows. We recommend using the _code_ flow as it's more
secure.
:::tip
This returns a URL to redirect the user to. This starts the OAuth flow.
:::
This returns a URL to the auth server. You can redirect the user to the URL to start the
OAuth flow.
For SPA apps, we recommend using the PKCE flow.
```ts {4}
const { challenge, url } = await client.authorize(
,
"code",
{ pkce: true }
)
```
This returns a redirect URL and a challenge that you need to use later to verify the code.
### exchange
**Type** (code: string, redirectURI: string, verifier?: string) => Promise<[ExchangeSuccess](#exchangesuccess) | [ExchangeError](#exchangeerror)>
Exchange the code for access and refresh tokens.
```ts
const exchanged = await client.exchange(, )
```
You call this after the user has been redirected back to your app after the OAuth flow.
:::tip
For SSR sites, the code is returned in the query parameter.
:::
So the code comes from the query parameter in the redirect URI. The redirect URI here is
the one that you passed in to the `authorize` call when starting the flow.
:::tip
For SPA sites, the code is returned through the URL hash.
:::
If you used the PKCE flow for an SPA app, the code is returned as a part of the redirect URL
hash.
```ts {4}
const exchanged = await client.exchange(
,
,
)
```
You also need to pass in the previously stored challenge verifier.
This method returns the access and refresh tokens. Or if it fails, it returns an error that
you can handle depending on the error.
```ts
import { InvalidAuthorizationCodeError } from "@openauthjs/openauth/error"
if (exchanged.err) {
if (exchanged.err instanceof InvalidAuthorizationCodeError) {
// handle invalid code error
}
else {
// handle other errors
}
}
const { access, refresh } = exchanged.tokens
```
### refresh
**Type** (refresh: string, opts?: [RefreshOptions](#refreshoptions)) => Promise<[RefreshSuccess](#refreshsuccess) | [RefreshError](#refresherror)>
Refreshes the tokens if they have expired. This is used in an SPA app to maintain the
session, without logging the user out.
```ts
const next = await client.refresh()
```
Can optionally take the access token as well. If passed in, this will skip the refresh
if the access token is still valid.
```ts
const next = await client.refresh(, { access: })
```
This returns the refreshed tokens only if they've been refreshed.
```ts
if (!next.err) {
// tokens are still valid
}
if (next.tokens) {
const { access, refresh } = next.tokens
}
```
Or if it fails, it returns an error that you can handle depending on the error.
```ts
import { InvalidRefreshTokenError } from "@openauthjs/openauth/error"
if (next.err) {
if (next.err instanceof InvalidRefreshTokenError) {
// handle invalid refresh token error
}
else {
// handle other errors
}
}
```
### verify
**Type** (subjects: [SubjectSchema](/docs/subject#subjectschema), token: string, options?: [VerifyOptions](#verifyoptions)) => Promise<[VerifyError](#verifyerror) | [VerifyResult](#verifyresult)>
Verify the token in the incoming request.
This is typically used for SSR sites where the token is stored in an HTTP only cookie. And
is passed to the server on every request.
```ts
const verified = await client.verify(, )
```
This takes the subjects that you had previously defined when creating the issuer.
:::tip
If the refresh token is passed in, it'll automatically refresh the access token.
:::
This can optionally take the refresh token as well. If passed in, it'll automatically
refresh the access token if it has expired.
```ts
const verified = await client.verify(, , { refresh: })
```
This returns the decoded subjects from the access token. And the tokens if they've been
refreshed.
```ts
// based on the subjects you defined earlier
console.log(verified.subject.properties.userID)
if (verified.tokens) {
const { access, refresh } = verified.tokens
}
```
Or if it fails, it returns an error that you can handle depending on the error.
```ts
import { InvalidRefreshTokenError } from "@openauthjs/openauth/error"
if (verified.err) {
if (verified.err instanceof InvalidRefreshTokenError) {
// handle invalid refresh token error
}
else {
// handle other errors
}
}
```
## AuthorizeOptions
-
[pkce?](#authorizeoptions.pkce) boolean
-
[provider?](#authorizeoptions.provider) string
pkce?
**Type** boolean
**Default** false
Enable the PKCE flow. This is for SPA apps.
```ts
{
pkce: true
}
```
provider?
**Type** string
The provider you want to use for the OAuth flow.
```ts
{
provider: "google"
}
```
If no provider is specified, the user is directed to a page where they can select from the
list of configured providers.
If there's only one provider configured, the user will be redirected to that.
## AuthorizeResult
-
challenge
**Type** [Challenge](#challenge)
The challenge that you can use to verify the code. This is for the PKCE flow for SPA apps.
This is an object that you _stringify_ and store it in session storage.
```ts
sessionStorage.setItem("challenge", JSON.stringify(challenge))
```
url
**Type** string
The URL to redirect the user to. This starts the OAuth flow.
For example, for SPA apps.
```ts
location.href = url
```
## Challenge
**Type** Object
The challenge that you can use to verify the code.
## ClientInput
-
[clientID](#clientinput.clientid) string
-
[fetch?](#clientinput.fetch) FetchLike
-
[issuer?](#clientinput.issuer) string
Configure the client.
clientID
**Type** string
The client ID. This is just a string to identify your app.
If you have a web app and a mobile app, you want to use different client IDs both.
```ts
{
clientID: "my-client"
}
```
fetch?
**Type** FetchLike
Optionally, override the internally used fetch function.
This is useful if you are using a polyfilled fetch function in your application and you
want the client to use it too.
issuer?
**Type** string
The URL of your OpenAuth server.
```ts
{
issuer: "https://auth.myserver.com"
}
```
## ExchangeError
-
Returned when the exchange fails.
err
**Type** [InvalidAuthorizationCodeError](/docs/issuer#invalidauthorizationcodeerror)
The type of error that occurred. You can handle this by checking the type.
```ts
import { InvalidAuthorizationCodeError } from "@openauthjs/openauth/error"
console.log(err instanceof InvalidAuthorizationCodeError)
```
## ExchangeSuccess
-
Returned when the exchange is successful.
err
**Type** false
This is always `false` when the exchange is successful.
tokens
**Type** [Tokens](#tokens)
The access and refresh tokens.
## RefreshError
-
Returned when the refresh fails.
err
**Type** [InvalidRefreshTokenError](/docs/issuer#invalidrefreshtokenerror) | [InvalidAccessTokenError](/docs/issuer#invalidaccesstokenerror)
The type of error that occurred. You can handle this by checking the type.
```ts
import { InvalidRefreshTokenError } from "@openauthjs/openauth/error"
console.log(err instanceof InvalidRefreshTokenError)
```
## RefreshOptions
-
[access?](#refreshoptions.access) string
access?
**Type** string
Optionally, pass in the access token.
## RefreshSuccess
-
Returned when the refresh is successful.
err
**Type** false
This is always `false` when the refresh is successful.
tokens?
**Type** [Tokens](#tokens)
Returns the refreshed tokens only if they've been refreshed.
If they are still valid, this will be `undefined`.
## Tokens
-
[access](#tokens.access) string
-
[expiresIn](#tokens.expiresin) number
-
[refresh](#tokens.refresh) string
The tokens returned by the auth server.
access
**Type** string
The access token.
expiresIn
**Type** number
The number of seconds until the access token expires.
refresh
**Type** string
The refresh token.
## VerifyError
-
Returned when the verify call fails.
err
**Type** [InvalidRefreshTokenError](/docs/issuer#invalidrefreshtokenerror) | [InvalidAccessTokenError](/docs/issuer#invalidaccesstokenerror)
The type of error that occurred. You can handle this by checking the type.
```ts
import { InvalidRefreshTokenError } from "@openauthjs/openauth/error"
console.log(err instanceof InvalidRefreshTokenError)
```
## VerifyOptions
-
[fetch?](#verifyoptions.fetch) FetchLike
-
[refresh?](#verifyoptions.refresh) string
fetch?
**Type** FetchLike
Optionally, override the internally used fetch function.
This is useful if you are using a polyfilled fetch function in your application and you
want the client to use it too.
refresh?
**Type** string
Optionally, pass in the refresh token.
If passed in, this will automatically refresh the access token if it has expired.
## VerifyResult
-
[err?](#verifyresult.err) undefined
-
[subject](#verifyresult.subject) Subject
-
[tokens?](#verifyresult.tokens) [Tokens](#tokens)
err?
**Type** undefined
This is always `undefined` when the verify is successful.
subject
**Type** Subject
The decoded subjects from the access token.
Has the same shape as the subjects you defined when creating the issuer.
tokens?
**Type** [Tokens](#tokens)
Returns the refreshed tokens only if they’ve been refreshed.
If they are still valid, this will be undefined.
================================================
FILE: www/src/content/docs/docs/index.mdx
================================================
---
title: OpenAuth
description: Introduction to OpenAuth.
---
import { Image } from "astro:assets"
import { Tabs, TabItem } from '@astrojs/starlight/components';
import themeDark from "./themes-dark.png"
import themeLight from "./themes-light.png"
[OpenAuth](/) is a standards-based auth provider for web apps, mobile apps, single pages apps, APIs, or 3rd party clients. It is currently in beta.
- **Universal**: You can deploy it as a standalone service or embed it into an existing application. It works with any framework or platform.
- **Self-hosted**: It runs entirely on your infrastructure and can be deployed on Node.js, Bun, AWS Lambda, or Cloudflare Workers.
- **Standards-based**: It implements the OAuth 2.0 spec and is based on web standards. So any OAuth client can use it.
- **Customizable**: It comes with prebuilt themeable UI that you can customize or opt out of.
:::tip
Check out the [launch video](https://www.youtube.com/watch?v=SSjNUuQ06tk) to learn more.
:::
---
## Get started
If you just want to get started as fast as possible you can jump straight into the [code examples](https://github.com/openauthjs/openauthjs/tree/master/examples) folder and copy paste away. There are also [SST components](https://sst.dev/docs/component/aws/auth) for deploying everything OpenAuth needs.
---
## Approach
While there are many open source solutions for auth, almost all of them are libraries that are meant to be embedded into a single application. Centralized auth servers typically are delivered as SaaS services - eg Auth0 or Clerk.
OpenAuth instead is a centralized auth server that runs on your own infrastructure and has been designed for ease of self hosting. It can be used to authenticate all of your applications - web apps, mobile apps, internal admin tools, etc.
It adheres mostly to OAuth 2.0 specifications - which means anything that can speak OAuth can use it to receive access and refresh tokens. When a client initiates an authorization flow, OpenAuth will hand off to one of the configured providers - this can be third party identity providers like Google, GitHub, etc or built in flows like email/password or pin code.
Because it follows these specifications it can even be used to issue credentials for third party applications - allowing you to implement "login with myapp" flows.
OpenAuth very intentionally does not attempt to solve user management. We've found that this is a very difficult problem given the wide range of databases and drivers that are used in the JS ecosystem. Additionally it's quite hard to build data abstractions that work for every use case. Instead, once a user has identified themselves OpenAuth will invoke a callback where you can implement your own user lookup/creation logic.
While OpenAuth tries to be mostly stateless, it does need to store a minimal amount of data (refresh tokens, password hashes, etc). However this has been reduced to a simple KV store with various implementations for zero overhead systems like Cloudflare KV and DynamoDB. You should never need to directly access any data that is stored in there.
There is also a themeable UI that you can use to get going without implementing any designs yourself. This is built on top of a lower level system so you can copy paste the default UI and tweak it or opt out entirely and implement your own.
Finally, OpenAuth is created by the maintainers of [SST](https://sst.dev) which is a tool to manage all the infrastructure for your app. It contains components for OpenAuth that make deploying it to AWS or Cloudflare as simple as it can get.
---
## Overview
We'll show how to deploy the OpenAuth server and then a sample app that uses it.
---
### Server
Start by importing the `issuer` function from the `@openauthjs/openauth` package.
```ts
import { issuer } from "@openauthjs/openauth"
```
OpenAuth is built on top of [Hono](https://github.com/honojs/hono) which is a minimal web framework that can run anywhere. The `issuer` function creates a Hono app with all of the auth server implemented that you can then deploy to AWS Lambda, Cloudflare Workers, or in a container running under Node.js or Bun.
The `issuer` function requires a few things:
```ts title="issuer.ts"
const app = issuer({
providers: { ... },
storage,
subjects,
success: async (ctx, value) => { ... }
})
```
First we need to define some providers that are enabled - these are either third party identity providers like Google, GitHub, etc or built in flows like email/password or pin code. You can also implement your own. Let's try the GitHub provider.
```ts title="issuer.ts"
import { GithubProvider } from "@openauthjs/openauth/provider/github"
const app = issuer({
providers: {
github: GithubProvider({
clientID: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
scopes: ["user:email"],
}),
},
// ...
})
```
Providers take some configuration - since this is a third party identity provider there is no UI to worry about and all it needs is a client ID, secret and some scopes. Let's add the password provider which is a bit more complicated.
```ts title="issuer.ts"
import { PasswordProvider } from "@openauthjs/openauth/provider/password"
const app = issuer({
providers: {
github: ...,
password: PasswordProvider(...),
},
// ...
})
```
The password provider is quite complicated as username/password involve a lot of flows so there are a lot of callbacks to implement. However you can opt into the default UI which has all of this already implemented for you. The only thing you have to specify is how to send a code for forgot password/email verification. In this case we'll log the code but you would send this over email.
```ts title="issuer.ts"
import { PasswordProvider } from "@openauthjs/openauth/provider/password"
import { PasswordUI } from "@openauthjs/openauth/ui/password"
const app = issuer({
providers: {
github: ...,
password: PasswordProvider(
PasswordUI({
sendCode: async (email, code) => {
console.log(email, code)
},
}),
),
},
// ...
})
```
Next up is the `subjects` field. Subjects are what the access token generated at the end of the auth flow will map to. Under the hood, the access token is a JWT that contains this data. You will likely just have a single subject to start but you can define additional ones for different types of users.
```ts title="subjects.ts"
import { object, string } from "valibot"
const subjects = createSubjects({
user: object({
userID: string(),
// may want to add workspaceID here if doing a multi-tenant app
workspaceID: string(),
}),
})
```
We are using Valibot here to define and validate the shape of the subject. You can use any validation library that follows the [standard-schema specification](https://github.com/standard-schema/standard-schema), including Zod `^3.24.0` and Valibot `^1.0.0`. See the full list of compatible libraries [here](https://standardschema.dev/#what-schema-libraries-implement-the-spec).
You typically will want to place subjects in its own file as it can be imported by all of your apps. You can pass it to the issuer in the `subjects` field.
```ts title="issuer.ts"
import { subjects } from "./subjects.js"
const app = issuer({
providers: { ... },
subjects,
...
})
```
Next we'll implement the `success` callback which receives the payload when a user successfully completes a provider flow.
```ts title="issuer.ts"
const app = issuer({
providers: { ... },
subjects,
async success(ctx, value) {
let userID
if (value.provider === "password") {
console.log(value.email)
userID = ... // lookup user or create them
}
if (value.provider === "github") {
console.log(value.tokenset.access)
userID = ... // lookup user or create them
}
return ctx.subject("user", {
userID,
'a workspace id'
})
}
})
```
All of this is typesafe - based on the configured providers you will receive different properties in the `value` object. Also the `subject` method will only accept properties. Most callbacks in OpenAuth can return a `Response` object. In this case if something goes wrong, you can return a `Response.redirect("...")` sending them to a different place or rendering an error.
Next we have the `storage` field which defines where things like refresh tokens and password hashes are stored. If on AWS we recommend DynamoDB, if on Cloudflare we recommend Cloudflare KV. We also have a MemoryStore used for testing.
```ts title="issuer.ts"
import { MemoryStorage } from "@openauthjs/openauth/storage/memory"
const app = issuer({
providers: { ... },
subjects,
async success(ctx, value) { ... },
storage: MemoryStorage(),
})
```
And now we are ready to deploy! Here's how you do that depending on your infrastructure.
```ts title="issuer.ts"
import { serve } from "@hono/node-server"
serve(app)
```
```ts title="issuer.ts"
import { handle } from "hono/aws-lambda"
export const handler = handle(app)
```
```ts title="issuer.ts"
export default app
```
```ts title="issuer.ts"
export default app
```
You now have a centralized OpenAuth server. Test it out by visiting `/.well-known/oauth-authorization-server` - you can see a live example [here](https://auth.terminal.shop/.well-known/oauth-authorization-server).
---
### Client
Since this is a standard OAuth server you can use any libraries for OAuth and it will work. OpenAuth does provide some light tooling for this although even a manual flow is pretty simple. You can create a client like this:
```ts title="client.ts"
import { createClient } from "@openauthjs/openauth/client"
const client = createClient({
clientID: "my-client",
issuer: "https://auth.myserver.com" // url to the OpenAuth server
})
```
#### SSR Sites
If your frontend has a server component you can use the code flow. Redirect the user here
```ts
const { url } = await client.authorize(
,
"code"
)
```
You can make up a `client_id` that represents your app. This will initiate the auth flow and user will be redirected to the `redirect_uri` you provided with a query parameter `code` which you can exchange for an access token.
```ts
// the redirect_uri is the original redirect_uri you passed in and is used for verification
const tokens = await client.exchange(query.get("code"), redirect_uri)
console.log(tokens.access, tokens.refresh)
```
You likely want to store both the access token and refresh token in an HTTP only cookie so they are sent up with future requests. Then you can use the `client` to verify the tokens.
```ts
const verified = await client.verify(subjects, cookies.get("access_token")!, {
refresh: cookies.get("refresh_token") || undefined,
})
console.log(
verified.subject.type,
verified.subject.properties,
verified.refresh,
verified.access,
)
```
Passing in the refresh token is optional but if you do, this function will automatically refresh the access token if it has expired. It will return a new access token and refresh token which you should set back into the cookies.
#### SPA Sites, Mobile apps, etc
In cases where you do not have a server, you can use the `token` flow with `pkce` on the frontend.
```ts
const { challenge, url } = await client.authorize(, "code", { pkce: true })
localStorage.setItem("challenge", JSON.stringify(challenge))
location.href = url
```
When the auth flow is complete the user's browser will be redirected to the `redirect_uri` with a `code` query parameter. You can then exchange the code for access/refresh tokens.
```ts
const challenge = JSON.parse(localStorage.getItem("challenge"))
const exchanged = await client.exchange(
query.get("code"),
redirect_uri,
challenge.verifier
)
if (exchanged.err) throw new Error("Invalid code")
localStorage.setItem("access_token", exchanged.tokens.access)
localStorage.setItem("refresh_token", exchanged.tokens.refresh)
```
Then when you make requests to your API you can include the access token in the `Authorization` header.
```ts
const accessToken = localStorage.getItem("access_token")
fetch("https://auth.example.com/api/user", {
headers: {
Authorization: `Bearer ${accessToken}`,
}
})
```
And then you can verify the access token on the server.
```ts
const verified = await client.verify(subjects, accessToken)
console.log(verified.subject)
```
================================================
FILE: www/src/content/docs/docs/issuer.mdx
================================================
---
title: Issuer
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/issuer.ts
description: Reference doc for the OpenAuth server.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
The `issuer` create an OpentAuth server, a [Hono](https://hono.dev) app that's
designed to run anywhere.
The `issuer` function requires a few things:
```ts title="issuer.ts"
import { issuer } from "@openauthjs/openauth"
const app = issuer({
providers: { ... },
storage,
subjects,
success: async (ctx, value) => { ... }
})
```
#### Add providers
You start by specifying the auth providers you are going to use. Let's say you want your users
to be able to authenticate with GitHub and with their email and password.
```ts title="issuer.ts"
import { GithubProvider } from "@openauthjs/openauth/provider/github"
import { PasswordProvider } from "@openauthjs/openauth/provider/password"
const app = issuer({
providers: {
github: GithubProvider({
// ...
}),
password: PasswordProvider({
// ...
}),
},
})
```
#### Handle success
The `success` callback receives the payload when a user completes a provider's auth flow.
```ts title="issuer.ts"
const app = issuer({
providers: { ... },
subjects,
async success(ctx, value) {
let userID
if (value.provider === "password") {
console.log(value.email)
userID = ... // lookup user or create them
}
if (value.provider === "github") {
console.log(value.tokenset.access)
userID = ... // lookup user or create them
}
return ctx.subject("user", {
userID
})
}
})
```
Once complete, the `issuer` issues the access tokens that a client can use. The `ctx.subject`
call is what is placed in the access token as a JWT.
#### Define subjects
You define the shape of these in the `subjects` field.
```ts title="subjects.ts"
import { object, string } from "valibot"
import { createSubjects } from "@openauthjs/openauth/subject"
const subjects = createSubjects({
user: object({
userID: string()
})
})
```
It's good to place this in a separate file since this'll be used in your client apps as well.
```ts title="issuer.ts"
import { subjects } from "./subjects.js"
const app = issuer({
providers: { ... },
subjects,
// ...
})
```
#### Deploy
Since `issuer` is a Hono app, you can deploy it anywhere Hono supports.
```ts title="issuer.ts"
import { serve } from "@hono/node-server"
serve(app)
```
```ts title="issuer.ts"
import { handle } from "hono/aws-lambda"
export const handler = handle(app)
```
```ts title="issuer.ts"
export default app
```
```ts title="issuer.ts"
export default app
```
---
## Methods
### issuer
```ts
issuer(input)
```
#### Parameters
-
input [IssuerInput](#issuerinput)
**Returns** Hono
Create an OpenAuth server, a Hono app.
## IssuerInput
-
providers
**Type** Record<string, Provider>
The providers that you want your OpenAuth server to support.
```ts title="issuer.ts"
import { GithubProvider } from "@openauthjs/openauth/provider/github"
issuer({
providers: {
github: GithubProvider()
}
})
```
The key is just a string that you can use to identify the provider. It's passed back to
the
`success`
callback.
You can also specify multiple providers.
```ts
{
providers: {
github: GithubProvider(),
google: GoogleProvider()
}
}
```
storage?
**Type** StorageAdapter
The storage adapter that you want to use.
```ts title="issuer.ts"
import { DynamoStorage } from "@openauthjs/openauth/storage/dynamo"
issuer({
storage: DynamoStorage()
// ...
})
```
subjects
**Type** [SubjectSchema](/docs/subject#subjectschema)
The shape of the subjects that you want to return.
```ts title="issuer.ts"
import { object, string } from "valibot"
import { createSubjects } from "@openauthjs/openauth/subject"
issuer({
subjects: createSubjects({
user: object({
userID: string()
})
})
// ...
})
```
theme?
**Type** [Theme](#theme)
The theme you want to use for the UI.
This includes the UI the user sees when selecting a provider. And the `PasswordUI` and
`CodeUI` that are used by the `PasswordProvider` and `CodeProvider`.
```ts title="issuer.ts"
import { THEME_SST } from "@openauthjs/openauth/ui/theme"
issuer({
theme: THEME_SST
// ...
})
```
Or define your own.
```ts title="issuer.ts"
import type { Theme } from "@openauthjs/openauth/ui/theme"
const MY_THEME: Theme = {
// ...
}
issuer({
theme: MY_THEME
// ...
})
```
ttl?
**Type** Object
Set the TTL, in seconds, for access and refresh tokens.
```ts
{
ttl: {
access: 60 * 60 * 24 * 30,
refresh: 60 * 60 * 24 * 365
}
}
```
access?
**Type** number
**Default** 30d
Interval in seconds where the access token is valid.
refresh?
**Type** number
**Default** 1y
Interval in seconds where the refresh token is valid.
retention?
**Type** number
**Default** 0s
Interval in seconds to retain refresh tokens for reuse detection.
reuse?
**Type** number
**Default** 60s
Interval in seconds where refresh token reuse is allowed. This helps mitigrate
concurrency issues.
allow?
**Type** (input: { audience: string, clientID: string, redirectURI: string }, req: Request) => Promise<boolean>
Override the logic for whether a client request is allowed to call the issuer.
By default, it uses the following:
- Allow if the `redirectURI` is localhost.
- Compare `redirectURI` to the request's hostname or the `x-forwarded-host` header. If they
are from the same sub-domain level, then allow.
```ts
{
allow: async (input, req) => {
// Allow all clients
return true
}
}
```
select?
**Type** (providers: Record<string, string>, req: Request) => Promise<Response>
**Default** Select()
Optionally, configure the UI that's displayed when the user visits the root URL of the
of the OpenAuth server.
```ts title="issuer.ts"
import { Select } from "@openauthjs/openauth/ui/select"
issuer({
select: Select({
providers: {
github: { hide: true },
google: { display: "Google" }
}
})
// ...
})
```
success
**Type** (response: [OnSuccessResponder](#onsuccessresponder), input: Result, req: Request) => Promise<Response>
The success callback that's called when the user completes the flow.
This is called after the user has been redirected back to your app after the OAuth flow.
```ts
{
success: async (ctx, value) => {
let userID
if (value.provider === "password") {
console.log(value.email)
userID = ... // lookup user or create them
}
if (value.provider === "github") {
console.log(value.tokenset.access)
userID = ... // lookup user or create them
}
return ctx.subject("user", {
userID
})
},
// ...
}
```
## OnSuccessResponder
-
Sets the subject payload in the JWT token and returns the response.
```ts
ctx.subject("user", {
userID
})
```
subject
**Type** (type: string, properties: any, opts?: { subject: string, ttl: { access: number, refresh: number } }) => Promise<Response>
The `type` is the type of the subject, that was defined in the `subjects` field.
The `properties` are the properties of the subject. This is the shape of the subject that
you defined in the `subjects` field.
## Errors
A list of errors that can be thrown by OpenAuth.
You can use these errors to check the type of error and handle it. For example.
```ts
import { InvalidAuthorizationCodeError } from "@openauthjs/openauth/error"
if (err instanceof InvalidAuthorizationCodeError) {
// handle invalid code error
}
```
---
### InvalidAccessTokenError
The given access token is invalid.
### InvalidAuthorizationCodeError
The given authorization code is invalid.
### InvalidRefreshTokenError
The given refresh token is invalid.
### InvalidSubjectError
The given subject is invalid.
### MissingParameterError
The given parameter is missing.
### MissingProviderError
The `provider` needs to be passed in.
### OauthError
The OAuth server returned an error.
### UnauthorizedClientError
The given client is not authorized to use the redirect URI that was passed in.
### UnknownStateError
The browser was in an unknown state.
This can happen when certain cookies have expired. Or the browser was switched in the middle
of the authentication flow.
================================================
FILE: www/src/content/docs/docs/provider/apple.mdx
================================================
---
title: AppleProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/apple.ts
description: Reference doc for the `AppleProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Apple. Supports both OAuth2 and OIDC.
#### Using OAuth
```ts {5-8}
import { AppleProvider } from "@openauthjs/openauth/provider/apple"
export default issuer({
providers: {
apple: AppleProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
#### Using OIDC
```ts {5-7}
import { AppleOidcProvider } from "@openauthjs/openauth/provider/apple"
export default issuer({
providers: {
apple: AppleOidcProvider({
clientID: "1234567890"
})
}
})
```
---
## Methods
### AppleOidcProvider
```ts
AppleOidcProvider(config)
```
#### Parameters
-
The config for the provider.
**Returns** Provider
Create an Apple OIDC provider.
This is useful if you just want to verify the user's email address.
```ts
AppleOidcProvider({
clientID: "1234567890"
})
```
### AppleProvider
```ts
AppleProvider(config)
```
#### Parameters
-
The config for the provider.
**Returns** Provider
Create an Apple OAuth2 provider.
```ts
AppleProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
```
## AppleConfig
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
## AppleOidcConfig
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
prompt: "consent"
}
}
```
scopes?
**Type** string[]
A list of OIDC scopes that you want to request.
```ts
{
scopes: ["openid", "profile", "email"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/code.mdx
================================================
---
title: CodeProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/code.ts
description: Reference doc for the `CodeProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Configures a provider that supports pin code authentication. This is usually paired with the
`CodeUI`.
```ts
import { CodeUI } from "@openauthjs/openauth/ui/code"
import { CodeProvider } from "@openauthjs/openauth/provider/code"
export default issuer({
providers: {
code: CodeProvider(
CodeUI({
copy: {
code_info: "We'll send a pin code to your email"
},
sendCode: (claims, code) => console.log(claims.email, code)
})
)
},
// ...
})
```
You can customize the provider using.
```ts {7-9}
const ui = CodeUI({
// ...
})
export default issuer({
providers: {
code: CodeProvider(
{ ...ui, length: 4 }
)
},
// ...
})
```
Behind the scenes, the `CodeProvider` expects callbacks that implements request handlers
that generate the UI for the following.
```ts
CodeProvider({
// ...
request: (req, state, form, error) => Promise
})
```
This allows you to create your own UI.
---
## Methods
### CodeProvider
```ts
CodeProvider(config)
```
#### Parameters
-
length?
**Type** number
**Default** 6
The length of the pin code.
request
**Type** (req: Request, state: [CodeProviderState](/docs/provider/code#codeproviderstate), form?: FormData, error?: [CodeProviderError](/docs/provider/code#codeprovidererror)) => Promise<Response>
The request handler to generate the UI for the code flow.
Takes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
and optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
ojects.
Also passes in the current `state` of the flow and any `error` that occurred.
Expects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object
in return.
sendCode
**Type** (claims: Record<string, string>, code: string) => Promise<void | [CodeProviderError](/docs/provider/code#codeprovidererror)>
Callback to send the pin code to the user.
```ts
{
sendCode: async (claims, code) => {
// Send the code through the email or phone number based on the claims
}
}
```
## CodeProviderError
**Type** { type: “invalid_code” } | { key: string, type: “invalid_claim”, value: string }
The errors that can happen on the code flow.
| Error | Description |
| ----- | ----------- |
| `invalid_code` | The code is invalid. |
| `invalid_claim` | The _claim_, email or phone number, is invalid. |
## CodeProviderState
**Type** { type: “start” } | { claims: Record<string, string>, code: string, resend: boolean, type: “code” }
The state of the code flow.
| State | Description |
| ----- | ----------- |
| `start` | The user is asked to enter their email address or phone number to start the flow. |
| `code` | The user needs to enter the pin code to verify their _claim_. |
================================================
FILE: www/src/content/docs/docs/provider/cognito.mdx
================================================
---
title: CognitoProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/cognito.ts
description: Reference doc for the `CognitoProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with a Cognito OAuth endpoint.
```ts {5-10}
import { CognitoProvider } from "@openauthjs/openauth/provider/cognito"
export default issuer({
providers: {
cognito: CognitoProvider({
domain: "your-domain.auth.us-east-1.amazoncognito.com",
region: "us-east-1",
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### CognitoProvider
```ts
CognitoProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
domain
**Type** string
The domain of the Cognito User Pool.
```ts
{
domain: "your-domain.auth.us-east-1.amazoncognito.com"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
region
**Type** string
The region the Cognito User Pool is in.
```ts
{
region: "us-east-1"
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/discord.mdx
================================================
---
title: DiscordProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/discord.ts
description: Reference doc for the `DiscordProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Discord.
```ts {5-8}
import { DiscordProvider } from "@openauthjs/openauth/provider/discord"
export default issuer({
providers: {
discord: DiscordProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### DiscordProvider
```ts
DiscordProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/facebook.mdx
================================================
---
title: FacebookProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/facebook.ts
description: Reference doc for the `FacebookProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Facebook. Supports both OAuth2 and OIDC.
#### Using OAuth
```ts {5-8}
import { FacebookProvider } from "@openauthjs/openauth/provider/facebook"
export default issuer({
providers: {
facebook: FacebookProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
#### Using OIDC
```ts {5-7}
import { FacebookOidcProvider } from "@openauthjs/openauth/provider/facebook"
export default issuer({
providers: {
facebook: FacebookOidcProvider({
clientID: "1234567890"
})
}
})
```
---
## Methods
### FacebookOidcProvider
```ts
FacebookOidcProvider(config)
```
#### Parameters
-
The config for the provider.
**Returns** Provider
Create a Facebook OIDC provider.
This is useful if you just want to verify the user's email address.
```ts
FacebookOidcProvider({
clientID: "1234567890"
})
```
### FacebookProvider
```ts
FacebookProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
## FacebookOidcConfig
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
prompt: "consent"
}
}
```
scopes?
**Type** string[]
A list of OIDC scopes that you want to request.
```ts
{
scopes: ["openid", "profile", "email"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/github.mdx
================================================
---
title: GithubProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/github.ts
description: Reference doc for the `GithubProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Github.
```ts {5-8}
import { GithubProvider } from "@openauthjs/openauth/provider/github"
export default issuer({
providers: {
github: GithubProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### GithubProvider
```ts
GithubProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/google.mdx
================================================
---
title: GoogleProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/google.ts
description: Reference doc for the `GoogleProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Google. Supports both OAuth2 and OIDC.
#### Using OAuth
```ts {5-8}
import { GoogleProvider } from "@openauthjs/openauth/provider/google"
export default issuer({
providers: {
google: GoogleProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
#### Using OIDC
```ts {5-7}
import { GoogleOidcProvider } from "@openauthjs/openauth/provider/google"
export default issuer({
providers: {
google: GoogleOidcProvider({
clientID: "1234567890"
})
}
})
```
---
## Methods
### GoogleOidcProvider
```ts
GoogleOidcProvider(config)
```
#### Parameters
-
The config for the provider.
**Returns** Provider
Create a Google OIDC provider.
This is useful if you just want to verify the user's email address.
```ts
GoogleOidcProvider({
clientID: "1234567890"
})
```
### GoogleProvider
```ts
GoogleProvider(config)
```
#### Parameters
-
The config for the provider.
**Returns** Provider
Create a Google OAuth2 provider.
```ts
GoogleProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
```
## GoogleConfig
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
## GoogleOidcConfig
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
prompt: "consent"
}
}
```
scopes?
**Type** string[]
A list of OIDC scopes that you want to request.
```ts
{
scopes: ["openid", "profile", "email"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/jumpcloud.mdx
================================================
---
title: JumpCloudProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/jumpcloud.ts
description: Reference doc for the `JumpCloudProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with JumpCloud.
```ts {5-8}
import { JumpCloudProvider } from "@openauthjs/openauth/provider/jumpcloud"
export default issuer({
providers: {
jumpcloud: JumpCloudProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### JumpCloudProvider
```ts
JumpCloudProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/keycloak.mdx
================================================
---
title: KeycloakProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/keycloak.ts
description: Reference doc for the `KeycloakProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with a Keycloak server.
```ts {5-10}
import { KeycloakProvider } from "@openauthjs/openauth/provider/keycloak"
export default issuer({
providers: {
keycloak: KeycloakProvider({
baseUrl: "https://your-keycloak-domain",
realm: "your-realm",
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### KeycloakProvider
```ts
KeycloakProvider(config)
```
#### Parameters
-
baseUrl
**Type** string
The base URL of the Keycloak server.
```ts
{
baseUrl: "https://your-keycloak-domain"
}
```
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
realm
**Type** string
The realm in the Keycloak server to authenticate against.
A realm in Keycloak is like a tenant or namespace that manages a set of
users, credentials, roles, and groups.
```ts
{
realm: "your-realm"
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/microsoft.mdx
================================================
---
title: MicrosoftProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/microsoft.ts
description: Reference doc for the `MicrosoftProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Microsoft. Supports both OAuth2 and OIDC.
#### Using OAuth
```ts {5-9}
import { MicrosoftProvider } from "@openauthjs/openauth/provider/microsoft"
export default issuer({
providers: {
microsoft: MicrosoftProvider({
tenant: "1234567890",
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
#### Using OIDC
```ts {5-7}
import { MicrosoftOidcProvider } from "@openauthjs/openauth/provider/microsoft"
export default issuer({
providers: {
microsoft: MicrosoftOidcProvider({
clientID: "1234567890"
})
}
})
```
---
## Methods
### MicrosoftOidcProvider
```ts
MicrosoftOidcProvider(config)
```
#### Parameters
-
The config for the provider.
**Returns** Provider
Create a Microsoft OIDC provider.
This is useful if you just want to verify the user's email address.
```ts
MicrosoftOidcProvider({
clientID: "1234567890"
})
```
### MicrosoftProvider
```ts
MicrosoftProvider(config)
```
#### Parameters
-
The config for the provider.
**Returns** Provider
Create a Microsoft OAuth2 provider.
```ts
MicrosoftProvider({
tenant: "1234567890",
clientID: "1234567890",
clientSecret: "0987654321"
})
```
## MicrosoftConfig
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
tenant
**Type** string
The tenant ID of the Microsoft account.
This is usually the same as the client ID.
```ts
{
tenant: "1234567890"
}
```
## MicrosoftOidcConfig
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
prompt: "consent"
}
}
```
scopes?
**Type** string[]
A list of OIDC scopes that you want to request.
```ts
{
scopes: ["openid", "profile", "email"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/oauth2.mdx
================================================
---
title: Oauth2Provider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/oauth2.ts
description: Reference doc for the `Oauth2Provider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this to connect authentication providers that support OAuth 2.0.
```ts {5-12}
import { Oauth2Provider } from "@openauthjs/openauth/provider/oauth2"
export default issuer({
providers: {
oauth2: Oauth2Provider({
clientID: "1234567890",
clientSecret: "0987654321",
endpoint: {
authorization: "https://auth.myserver.com/authorize",
token: "https://auth.myserver.com/token"
}
})
}
})
```
---
## Methods
### Oauth2Provider
```ts
Oauth2Provider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
endpoint
**Type** Object
The URLs of the authorization and token endpoints.
```ts
{
endpoint: {
authorization: "https://auth.myserver.com/authorize",
token: "https://auth.myserver.com/token"
}
}
```
authorization
**Type** string
The URL of the authorization endpoint.
token
**Type** string
The URL of the token endpoint.
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/oidc.mdx
================================================
---
title: OidcProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/oidc.ts
description: Reference doc for the `OidcProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this to connect authentication providers that support OIDC.
```ts {5-8}
import { OidcProvider } from "@openauthjs/openauth/provider/oidc"
export default issuer({
providers: {
oauth2: OidcProvider({
clientId: "1234567890",
issuer: "https://auth.myserver.com"
})
}
})
```
---
## Methods
### OidcProvider
```ts
OidcProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
issuer
**Type** string
The URL of your authorization server.
```ts
{
issuer: "https://auth.myserver.com"
}
```
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
prompt: "consent"
}
}
```
scopes?
**Type** string[]
A list of OIDC scopes that you want to request.
```ts
{
scopes: ["openid", "profile", "email"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/password.mdx
================================================
---
title: PasswordProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/password.ts
description: Reference doc for the `PasswordProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Configures a provider that supports username and password authentication. This is usually
paired with the `PasswordUI`.
```ts
import { PasswordUI } from "@openauthjs/openauth/ui/password"
import { PasswordProvider } from "@openauthjs/openauth/provider/password"
export default issuer({
providers: {
password: PasswordProvider(
PasswordUI({
copy: {
error_email_taken: "This email is already taken."
},
sendCode: (email, code) => console.log(email, code)
})
)
},
// ...
})
```
Behind the scenes, the `PasswordProvider` expects callbacks that implements request handlers
that generate the UI for the following.
```ts
PasswordProvider({
// ...
login: (req, form, error) => Promise
register: (req, state, form, error) => Promise
change: (req, state, form, error) => Promise
})
```
This allows you to create your own UI for each of these screens.
---
## Methods
### PasswordProvider
```ts
PasswordProvider(config)
```
#### Parameters
-
**Returns** Provider
## PasswordChangeError
**Type** { type: “invalid_email” } | { type: “invalid_code” } | { type: “invalid_password” } | { type: “password_mismatch” } | { message: string, type: “validation_error” }
The errors that can happen on the change password screen.
| Error | Description |
| ----- | ----------- |
| `invalid_email` | The email is invalid. |
| `invalid_code` | The code is invalid. |
| `invalid_password` | The password is invalid. |
| `password_mismatch` | The passwords do not match. |
## PasswordChangeState
**Type** { redirect: string, type: “start” } | { code: string, email: string, redirect: string, type: “code” } | { email: string, redirect: string, type: “update” }
The state of the password change flow.
| State | Description |
| ----- | ----------- |
| `start` | The user is asked to enter their email address to start the flow. |
| `code` | The user needs to enter the pin code to verify their email. |
| `update` | The user is asked to enter their new password and confirm it. |
## PasswordConfig
-
change
**Type** (req: Request, state: [PasswordChangeState](/docs/provider/password#passwordchangestate), form?: FormData, error?: [PasswordChangeError](/docs/provider/password#passwordchangeerror)) => Promise<Response>
The request handler to generate the UI for the change password screen.
Takes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
and optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
ojects.
Also passes in the current `state` of the flow and any `error` that occurred.
Expects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object
in return.
login
**Type** (req: Request, form?: FormData, error?: [PasswordLoginError](/docs/provider/password#passwordloginerror)) => Promise<Response>
The request handler to generate the UI for the login screen.
Takes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
and optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
ojects.
In case of an error, this is called again with the `error`.
Expects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object
in return.
register
**Type** (req: Request, state: [PasswordRegisterState](/docs/provider/password#passwordregisterstate), form?: FormData, error?: [PasswordRegisterError](/docs/provider/password#passwordregistererror)) => Promise<Response>
The request handler to generate the UI for the register screen.
Takes the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
and optionally [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
ojects.
Also passes in the current `state` of the flow and any `error` that occurred.
Expects the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object
in return.
sendCode
**Type** (email: string, code: string) => Promise<void>
Callback to send the confirmation pin code to the user.
```ts
{
sendCode: async (email, code) => {
// Send an email with the code
}
}
```
validatePassword?
**Type** [StandardSchema](https://github.com/standard-schema/standard-schema) | (password: string) => undefined | string | Promise<undefined | string>
Callback to validate the password on sign up and password reset.
```ts
{
validatePassword: (password) => {
return password.length < 8 ? "Password must be at least 8 characters" : undefined
}
}
```
## PasswordLoginError
**Type** { type: “invalid_password” } | { type: “invalid_email” }
The errors that can happen on the login screen.
| Error | Description |
| ----- | ----------- |
| `invalid_email` | The email is invalid. |
| `invalid_password` | The password is invalid. |
## PasswordRegisterError
**Type** { type: “invalid_code” } | { type: “email_taken” } | { type: “invalid_email” } | { type: “invalid_password” } | { type: “password_mismatch” } | { message: string, type: “validation_error” }
The errors that can happen on the register screen.
| Error | Description |
| ----- | ----------- |
| `email_taken` | The email is already taken. |
| `invalid_email` | The email is invalid. |
| `invalid_code` | The code is invalid. |
| `invalid_password` | The password is invalid. |
| `password_mismatch` | The passwords do not match. |
## PasswordRegisterState
**Type** { type: “start” } | { code: string, email: string, password: string, type: “code” }
The states that can happen on the register screen.
| State | Description |
| ----- | ----------- |
| `start` | The user is asked to enter their email address and password to start the flow. |
| `code` | The user needs to enter the pin code to verify their email. |
================================================
FILE: www/src/content/docs/docs/provider/slack.mdx
================================================
---
title: SlackProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/slack.ts
description: Reference doc for the `SlackProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Slack.
```ts {5-10}
import { SlackProvider } from "@openauthjs/openauth/provider/slack"
export default issuer({
providers: {
slack: SlackProvider({
team: "T1234567890",
clientID: "1234567890",
clientSecret: "0987654321",
scopes: ["openid", "email", "profile"]
})
}
})
```
---
## Methods
### SlackProvider
```ts
SlackProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** (“email” | “profile” | “openid”)[]
The scopes to request from the user.
| Scope | Description |
|-|-|
| `email` | Grants permission to access the user's email address. |
| `profile` | Grants permission to access the user's profile information. |
| `openid` | Grants permission to use OpenID Connect to verify the user's identity. |
team
**Type** string
The workspace the user is intending to authenticate.
If that workspace has been previously authenticated, the user will be signed in directly,
bypassing the consent screen.
================================================
FILE: www/src/content/docs/docs/provider/spotify.mdx
================================================
---
title: SpotifyProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/spotify.ts
description: Reference doc for the `SpotifyProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Spotify.
```ts {5-8}
import { SpotifyProvider } from "@openauthjs/openauth/provider/spotify"
export default issuer({
providers: {
spotify: SpotifyProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### SpotifyProvider
```ts
SpotifyProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/twitch.mdx
================================================
---
title: TwitchProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/twitch.ts
description: Reference doc for the `TwitchProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Twitch.
```ts {5-8}
import { TwitchProvider } from "@openauthjs/openauth/provider/twitch"
export default issuer({
providers: {
twitch: TwitchProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### TwitchProvider
```ts
TwitchProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/x.mdx
================================================
---
title: XProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/x.ts
description: Reference doc for the `XProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with X.com.
```ts {5-8}
import { XProvider } from "@openauthjs/openauth/provider/x"
export default issuer({
providers: {
x: XProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### XProvider
```ts
XProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/provider/yahoo.mdx
================================================
---
title: YahooProvider
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/provider/yahoo.ts
description: Reference doc for the `YahooProvider`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Use this provider to authenticate with Yahoo.
```ts {5-8}
import { YahooProvider } from "@openauthjs/openauth/provider/yahoo"
export default issuer({
providers: {
yahoo: YahooProvider({
clientID: "1234567890",
clientSecret: "0987654321"
})
}
})
```
---
## Methods
### YahooProvider
```ts
YahooProvider(config)
```
#### Parameters
-
clientID
**Type** string
The client ID.
This is just a string to identify your app.
```ts
{
clientID: "my-client"
}
```
clientSecret
**Type** string
The client secret.
This is a private key that's used to authenticate your app. It should be kept secret.
```ts
{
clientSecret: "0987654321"
}
```
pkce?
**Type** boolean
**Default** false
Whether to use PKCE (Proof Key for Code Exchange) for the authorization code flow.
Some providers like x.com require this.
query?
**Type** Record<string, string>
Any additional parameters that you want to pass to the authorization endpoint.
```ts
{
query: {
access_type: "offline",
prompt: "consent"
}
}
```
scopes
**Type** string[]
A list of OAuth scopes that you want to request.
```ts
{
scopes: ["email", "profile"]
}
```
================================================
FILE: www/src/content/docs/docs/start/sst.mdx
================================================
---
title: OpenAuth with SST and Next.js
description: Add OpenAuth to your Next.js app and deploy it with SST.
---
import { Image } from "astro:assets"
import nextAppDark from "./nextjs-dark.png"
import nextAppLight from "./nextjs-light.png"
We are going to create a new Next.js app, add authentication to it with OpenAuth, and deploy it with SST.
:::tip[View source]
You can [view the source](https://github.com/openauthjs/openauth/tree/master/examples/quickstart/sst) of this example in our repo.
:::
We are going to authenticate users by sending them a code to verify their email address.
---
## 1. Create a project
Let's start by creating our Next.js app and starting it in dev mode.
```bash
npx create-next-app@latest oa-nextjs
cd oa-nextjs
```
We are picking **TypeScript** and not selecting **ESLint**.
---
##### Init SST
Now let's initialize SST in our app.
```bash
npx sst@latest init
```
Select the defaults and pick **AWS**. This'll create a `sst.config.ts` file in your project root.
---
## 2. Add OpenAuth server
Next, let's add a directory for our OpenAuth server.
```bash
mkdir auth
```
Add our OpenAuth server to a `auth/index.ts` file.
```ts title="auth/index.ts"
import { handle } from "hono/aws-lambda"
import { issuer } from "@openauthjs/openauth"
import { CodeUI } from "@openauthjs/openauth/ui/code"
import { CodeProvider } from "@openauthjs/openauth/provider/code"
import { MemoryStorage } from "@openauthjs/openauth/storage/memory"
import { subjects } from "./subjects"
async function getUser(email: string) {
// Get user from database and return user ID
return "123"
}
const app = issuer({
subjects,
storage: MemoryStorage(),
// Remove after setting custom domain
allow: async () => true,
providers: {
code: CodeProvider(
CodeUI({
sendCode: async (email, code) => {
console.log(email, code)
},
}),
),
},
success: async (ctx, value) => {
if (value.provider === "code") {
return ctx.subject("user", {
id: await getUser(value.claims.email)
})
}
throw new Error("Invalid provider")
},
})
export const handler = handle(app)
```
---
##### Define subjects
We are also going to define our subjects. Add the following to a `auth/subjects.ts` file.
```ts title="auth/subjects.ts"
import { object, string } from "valibot"
import { createSubjects } from "@openauthjs/openauth/subject"
export const subjects = createSubjects({
user: object({
id: string(),
}),
})
```
Let's install our dependencies.
```bash
npm install @openauthjs/openauth valibot hono
```
---
##### Add Auth component
Now let's add this to our SST app. Replace the `run` function in `sst.config.ts` with the following.
```ts title="sst.config.ts" {6}
const auth = new sst.aws.Auth("MyAuth", {
issuer: "auth/index.handler",
});
new sst.aws.Nextjs("MyWeb", {
link: [auth]
});
```
This is defining our OpenAuth component and linking it to our Next.js app.
---
##### Start dev mode
Run the following to start dev mode. This'll start SST, your Next.js app, and your OpenAuth server.
```bash
npx sst dev
```
Once complete, it should give you the URL of your OpenAuth server.
```bash
✓ Complete
MyAuth: https://fv62a3niazbkrazxheevotace40affnk.lambda-url.us-east-1.on.aws
```
Also click on **MyWeb** in the sidebar and open your Next.js app by going to `http://localhost:3000`.
---
## 3. Add OpenAuth client
Next, let's add our OpenAuth client to our Next.js app. Add the following to `app/auth.ts`.
```ts title="app/auth.ts" {7}
import { Resource } from "sst"
import { createClient } from "@openauthjs/openauth/client"
import { cookies as getCookies } from "next/headers"
export const client = createClient({
clientID: "nextjs",
issuer: Resource.MyAuth.url
})
export async function setTokens(access: string, refresh: string) {
const cookies = await getCookies()
cookies.set({
name: "access_token",
value: access,
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 34560000,
})
cookies.set({
name: "refresh_token",
value: refresh,
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 34560000,
})
}
```
Here we are _linking_ to our auth server. And once the user is authenticated, we'll be saving their access and refresh tokens in _http only_ cookies.
---
##### Add auth actions
Let's add the server actions that our Next.js app will need to authenticate users. Add the following to `app/actions.ts`.
```ts title="app/actions.ts"
"use server"
import { redirect } from "next/navigation"
import { headers as getHeaders, cookies as getCookies } from "next/headers"
import { subjects } from "../auth/subjects"
import { client, setTokens } from "./auth"
export async function auth() {
const cookies = await getCookies()
const accessToken = cookies.get("access_token")
const refreshToken = cookies.get("refresh_token")
if (!accessToken) {
return false
}
const verified = await client.verify(subjects, accessToken.value, {
refresh: refreshToken?.value,
})
if (verified.err) {
return false
}
if (verified.tokens) {
await setTokens(verified.tokens.access, verified.tokens.refresh)
}
return verified.subject
}
export async function login() {
const cookies = await getCookies()
const accessToken = cookies.get("access_token")
const refreshToken = cookies.get("refresh_token")
if (accessToken) {
const verified = await client.verify(subjects, accessToken.value, {
refresh: refreshToken?.value,
})
if (!verified.err && verified.tokens) {
await setTokens(verified.tokens.access, verified.tokens.refresh)
redirect("/")
}
}
const headers = await getHeaders()
const host = headers.get("host")
const protocol = host?.includes("localhost") ? "http" : "https"
const { url } = await client.authorize(`${protocol}://${host}/api/callback`, "code")
redirect(url)
}
export async function logout() {
const cookies = await getCookies()
cookies.delete("access_token")
cookies.delete("refresh_token")
redirect("/")
}
```
This is adding an `auth` action that checks if a user is authenticated, `login` that starts the OAuth flow, and `logout` that clears the session.
---
##### Add callback route
When the OpenAuth flow is complete, users will be redirected back to our Next.js app. Let's add a callback route to handle this in `app/api/callback/route.ts`.
```ts title="app/api/callback/route.ts"
import { client, setTokens } from "../../auth"
import { type NextRequest, NextResponse } from "next/server"
export async function GET(req: NextRequest) {
const url = new URL(req.url)
const code = url.searchParams.get("code")
const exchanged = await client.exchange(code!, `${url.origin}/api/callback`)
if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 })
await setTokens(exchanged.tokens.access, exchanged.tokens.refresh)
return NextResponse.redirect(`${url.origin}/`)
}
```
Once the user is authenticated, we redirect them to the root of our app.
---
## 4. Add auth to app
Now we are ready to add authentication to our app. Replace the `` component in `app/page.tsx` with the following.
```tsx title="app/page.tsx"
import { auth, login, logout } from "./actions"
export default async function Home() {
const subject = await auth()
return (
{subject ? (
<>
Logged in as {subject.properties.id}.
And then check out app/page.tsx.
>
) : (
<>
Login with your email and password.
And then check out app/page.tsx.
>
)}
{subject ? (
) : (
)}
)
}
```
Let's also add these styles to `app/page.module.css`.
```css title="app/page.module.css"
.ctas button {
appearance: none;
background: transparent;
border-radius: 128px;
height: 48px;
padding: 0 20px;
border: none;
border: 1px solid transparent;
transition:
background 0.2s,
color 0.2s,
border-color 0.2s;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
line-height: 20px;
font-weight: 500;
}
button.primary {
background: var(--foreground);
color: var(--background);
gap: 8px;
}
button.secondary {
border-color: var(--gray-alpha-200);
min-width: 180px;
}
```
---
## 4. Test your app
Head to `http://localhost:3000` and click the login button, you should be redirected to the OpenAuth server asking you to put in your email.
If you check the **Functions** tab in your `sst dev` session, you'll see the code being console logged. You can use this code to login.
This should log you in and print your user ID.
---
## Deploy your app
Now let's deploy your app to AWS.
```bash
npx sst deploy --stage production
```
You can use any stage name here but it's good to create a new stage for production.
```bash
✓ Complete
MyAuth: https://vp3honbl3od4gmo7mei37mchky0waxew.lambda-url.us-east-1.on.aws
MyWeb: https://d2fjg1rqbqi95t.cloudfront.net
```
Congrats! Your app and your OpenAuth server should now be live!
================================================
FILE: www/src/content/docs/docs/start/standalone.mdx
================================================
---
title: OpenAuth with Next.js
description: Use OpenAuth to add authentication to your Next.js app.
---
import { Image } from "astro:assets"
import nextAppDark from "./nextjs-dark.png"
import nextAppLight from "./nextjs-light.png"
We are going to create a new Next.js app and add authentication to it with OpenAuth.
:::tip[View source]
You can [view the source](https://github.com/openauthjs/openauth/tree/master/examples/quickstart/standalone) of this example in our repo.
:::
We are going to authenticate users by sending them a code to verify their email address.
---
## 1. Create a project
Let's start by creating our Next.js app and starting it in dev mode.
```bash
bun create next-app oa-nextjs
cd oa-nextjs
bun dev
```
We are picking **TypeScript** and not selecting **ESLint**.
This will start our Next.js app at `http://localhost:3000`.
---
## 2. Add OpenAuth server
Next, let's add a directory for our OpenAuth server.
```bash
mkdir auth
```
Add our OpenAuth server to a `auth/index.ts` file.
```ts title="auth/index.ts"
import { issuer } from "@openauthjs/openauth"
import { CodeUI } from "@openauthjs/openauth/ui/code"
import { CodeProvider } from "@openauthjs/openauth/provider/code"
import { MemoryStorage } from "@openauthjs/openauth/storage/memory"
import { subjects } from "./subjects"
async function getUser(email: string) {
// Get user from database and return user ID
return "123"
}
export default issuer({
subjects,
storage: MemoryStorage(),
providers: {
code: CodeProvider(
CodeUI({
sendCode: async (email, code) => {
console.log(email, code)
},
}),
),
},
success: async (ctx, value) => {
if (value.provider === "code") {
return ctx.subject("user", {
id: await getUser(value.claims.email)
})
}
throw new Error("Invalid provider")
},
})
```
---
##### Define subjects
We are also going to define our subjects. Add the following to a `auth/subjects.ts` file.
```ts title="auth/subjects.ts"
import { object, string } from "valibot"
import { createSubjects } from "@openauthjs/openauth/subject"
export const subjects = createSubjects({
user: object({
id: string(),
}),
})
```
Let's install our dependencies.
```bash
bun add @openauthjs/openauth valibot
```
And add a script to start our auth server to `package.json`.
```js title="package.json"
"dev:auth": "PORT=3001 bun run --hot auth/index.ts",
```
Now run the auth server in a separate terminal.
```bash
bun dev:auth
```
This will start our auth server at `http://localhost:3001`.
---
## 3. Add OpenAuth client
Next, let's add our OpenAuth client to our Next.js app. Add the following to `app/auth.ts`.
```ts title="app/auth.ts"
import { createClient } from "@openauthjs/openauth/client"
import { cookies as getCookies } from "next/headers"
export const client = createClient({
clientID: "nextjs",
issuer: "http://localhost:3001",
})
export async function setTokens(access: string, refresh: string) {
const cookies = await getCookies()
cookies.set({
name: "access_token",
value: access,
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 34560000,
})
cookies.set({
name: "refresh_token",
value: refresh,
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 34560000,
})
}
```
Here we are assuming that our auth server is running at `http://localhost:3001`. Once the user is authenticated, we'll be saving their access and refresh tokens in _http only_ cookies.
---
##### Add auth actions
Let's add the server actions that our Next.js app will need to authenticate users. Add the following to `app/actions.ts`.
```ts title="app/actions.ts"
"use server"
import { redirect } from "next/navigation"
import { headers as getHeaders, cookies as getCookies } from "next/headers"
import { subjects } from "../auth/subjects"
import { client, setTokens } from "./auth"
export async function auth() {
const cookies = await getCookies()
const accessToken = cookies.get("access_token")
const refreshToken = cookies.get("refresh_token")
if (!accessToken) {
return false
}
const verified = await client.verify(subjects, accessToken.value, {
refresh: refreshToken?.value,
})
if (verified.err) {
return false
}
if (verified.tokens) {
await setTokens(verified.tokens.access, verified.tokens.refresh)
}
return verified.subject
}
export async function login() {
const cookies = await getCookies()
const accessToken = cookies.get("access_token")
const refreshToken = cookies.get("refresh_token")
if (accessToken) {
const verified = await client.verify(subjects, accessToken.value, {
refresh: refreshToken?.value,
})
if (!verified.err && verified.tokens) {
await setTokens(verified.tokens.access, verified.tokens.refresh)
redirect("/")
}
}
const headers = await getHeaders()
const host = headers.get("host")
const protocol = host?.includes("localhost") ? "http" : "https"
const { url } = await client.authorize(`${protocol}://${host}/api/callback`, "code")
redirect(url)
}
export async function logout() {
const cookies = await getCookies()
cookies.delete("access_token")
cookies.delete("refresh_token")
redirect("/")
}
```
This is adding an `auth` action that checks if a user is authenticated, `login` that starts the OAuth flow, and `logout` that clears the session.
---
##### Add callback route
When the OpenAuth flow is complete, users will be redirected back to our Next.js app. Let's add a callback route to handle this in `app/api/callback/route.ts`.
```ts title="app/api/callback/route.ts"
import { client, setTokens } from "../../auth"
import { type NextRequest, NextResponse } from "next/server"
export async function GET(req: NextRequest) {
const url = new URL(req.url)
const code = url.searchParams.get("code")
const exchanged = await client.exchange(code!, `${url.origin}/api/callback`)
if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 })
await setTokens(exchanged.tokens.access, exchanged.tokens.refresh)
return NextResponse.redirect(`${url.origin}/`)
}
```
Once the user is authenticated, we redirect them to the root of our app.
---
## 4. Add auth to app
Now we are ready to add authentication to our app. Replace the `` component in `app/page.tsx` with the following.
```tsx title="app/page.tsx"
import { auth, login, logout } from "./actions"
export default async function Home() {
const subject = await auth()
return (
{subject ? (
<>
Logged in as {subject.properties.id}.
And then check out app/page.tsx.
>
) : (
<>
Login with your email and password.
And then check out app/page.tsx.
>
)}
{subject ? (
) : (
)}
)
}
```
Let's also add these styles to `app/page.module.css`.
```css title="app/page.module.css"
.ctas button {
appearance: none;
background: transparent;
border-radius: 128px;
height: 48px;
padding: 0 20px;
border: none;
border: 1px solid transparent;
transition:
background 0.2s,
color 0.2s,
border-color 0.2s;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
line-height: 20px;
font-weight: 500;
}
button.primary {
background: var(--foreground);
color: var(--background);
gap: 8px;
}
button.secondary {
border-color: var(--gray-alpha-200);
min-width: 180px;
}
```
---
## 4. Test your app
Head to `http://localhost:3000` and click the login button, you should be redirected to the OpenAuth server asking you to put in your email.
If you check the terminal running the auth server, you'll see the code being console logged. You can use this code to login.
This should log you in and print your user ID.
---
## Deploy your app
To are now ready to deploy your app and your OpenAuth server. A couple of changes you'll need to make.
1. Use a more persistent `storage` like [DynamoDB](https://aws.amazon.com/dynamodb/) or [Cloudflare KV](https://developers.cloudflare.com/kv/) in your `auth/index.ts`.
2. Instead of printing out the code, email that to the user.
3. Finally, in your `app/auth.ts`, use the deployed auth server URL instead of `http://localhost:3001`.
You can also check out the [**SST quick start**](/docs/start/sst) for a fully deployed example.
================================================
FILE: www/src/content/docs/docs/storage/cloudflare.mdx
================================================
---
title: Cloudflare KV
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/storage/cloudflare.ts
description: Reference doc for the Cloudflare KV storage adapter.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Configure OpenAuth to use [Cloudflare KV](https://developers.cloudflare.com/kv/) as a
storage adapter.
```ts
import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare"
const storage = CloudflareStorage({
namespace: "my-namespace"
})
export default issuer({
storage,
// ...
})
```
---
## Methods
### CloudflareStorage
```ts
CloudflareStorage(options)
```
#### Parameters
-
Configure the DynamoDB table that's created.
```ts
{
table: "my-table",
pk: "pk",
sk: "sk"
}
```
endpoint?
**Type** string
**Default** "https://dynamodb.{region}.amazonaws.com"
Endpoint URL for the DynamoDB service. Useful for local testing.
pk?
**Type** string
**Default** "pk"
The primary key column name.
sk?
**Type** string
**Default** "sk"
The sort key column name.
table
**Type** string
The name of the DynamoDB table.
ttl?
**Type** string
**Default** "expiry"
The name of the time to live attribute.
================================================
FILE: www/src/content/docs/docs/storage/memory.mdx
================================================
---
title: Memory
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/storage/memory.ts
description: Reference doc for the Memory storage adapter.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Configure OpenAuth to use a simple in-memory store.
:::caution
This is not meant to be used in production.
:::
This is useful for testing and development. It's not meant to be used in production.
```ts
import { MemoryStorage } from "@openauthjs/openauth/storage/memory"
const storage = MemoryStorage()
export default issuer({
storage,
// ...
})
```
Optionally, you can persist the store to a file.
```ts
MemoryStorage({
persist: "./persist.json"
})
```
---
## Methods
### MemoryStorage
```ts
MemoryStorage(input?)
```
#### Parameters
-
Configure the memory store.
persist?
**Type** string
Optionally, backup the store to a file. So it'll be persisted when the issuer restarts.
```ts
{
persist: "./persist.json"
}
```
Subjects are what the access token generated at the end of the auth flow will map to. Under
the hood, the access token is a JWT that contains this data.
#### Define subjects
```ts title="subjects.ts"
import { object, string } from "valibot"
const subjects = createSubjects({
user: object({
userID: string()
})
})
```
We are using [valibot](https://github.com/fabian-hiller/valibot) here. You can use any
validation library that's following the
[standard-schema specification](https://github.com/standard-schema/standard-schema).
:::tip
You typically want to place subjects in its own file so it can be imported by all of your apps.
:::
You can start with one subject. Later you can add more for different types of users.
#### Set the subjects
Then you can pass it to the `issuer`.
```ts title="issuer.ts"
import { subjects } from "./subjects"
const app = issuer({
providers: { ... },
subjects,
// ...
})
```
#### Add the subject payload
When your user completes the flow, you can add the subject payload in the `success` callback.
```ts title="issuer.ts"
const app = issuer({
providers: { ... },
subjects,
async success(ctx, value) {
let userID
if (value.provider === "password") {
console.log(value.email)
userID = ... // lookup user or create them
}
return ctx.subject("user", {
userID
})
},
// ...
})
```
Here we are looking up the userID from our database and adding it to the subject payload.
:::caution
You should only store properties that won't change for the lifetime of the user.
:::
Since these will be stored in the access token, you should avoid storing information
that'll change often. For example, if you store the user's username, you'll need to
revoke the access token when the user changes their username.
#### Decode the subject
Now when your user logs in, you can use the OpenAuth client to decode the subject. For
example, in our SSR app we can do the following.
```ts title="app/page.tsx"
import { subjects } from "../subjects"
const verified = await client.verify(subjects, cookies.get("access_token")!)
console.log(verified.subject.properties.userID)
```
All this is typesafe based on the shape of the subjects you defined.
---
## Methods
### createSubjects
```ts
createSubjects(types)
```
#### Parameters
-
**Returns** [SubjectSchema](/docs/subject#subjectschema)
Create a subject schema.
```ts
const subjects = createSubjects({
user: object({
userID: string()
}),
admin: object({
workspaceID: string()
})
})
```
This is using [valibot](https://github.com/fabian-hiller/valibot) to define the shape of the
subjects. You can use any validation library that's following the
[standard-schema specification](https://github.com/standard-schema/standard-schema).
## SubjectSchema
**Type** Record<string, [v1.StandardSchema](https://github.com/standard-schema/standard-schema)>
Subject schema is a map of types that are used to define the subjects.
================================================
FILE: www/src/content/docs/docs/ui/code.mdx
================================================
---
title: CodeUI
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/ui/code.tsx
description: Reference doc for the `CodeUI`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Configure the UI that's used by the Code provider.
```ts {1,7-12}
import { CodeUI } from "@openauthjs/openauth/ui/code"
import { CodeProvider } from "@openauthjs/openauth/provider/code"
export default issuer({
providers: {
code: CodeAdapter(
CodeUI({
copy: {
code_info: "We'll send a pin code to your email"
},
sendCode: (claims, code) => console.log(claims.email, code)
})
)
},
// ...
})
```
---
## Methods
### CodeUI
```ts
CodeUI(props)
```
#### Parameters
-
props [CodeUIOptions](#codeuioptions)
Configure the UI.
**Returns** CodeProviderOptions
Creates a UI for the Code provider flow.
## CodeUICopy
**Type** any
## CodeUIOptions
-
Configure the password UI.
copy?
**Type** Object
Custom copy for the UI.
button_continue
**Type** string
**Default** “Continue”
Copy for the continue button.
code_didnt_get
**Type** string
**Default** “Didn't get code?”
Copy for the link to resend the code.
code_info
**Type** string
**Default** “We'll send a pin code to your email.”
Copy informing that the pin code will be emailed.
code_invalid
**Type** string
**Default** “Invalid code”
Error message when the code is invalid.
code_placeholder
**Type** string
**Default** “Code”
Copy for the pin code input.
code_resend
**Type** string
**Default** “Resend”
Copy for the resend button.
code_resent
**Type** string
**Default** “Code resent to ”
Copy for when the code was resent.
code_sent
**Type** string
**Default** “Code sent to ”
Copy for when the code was sent.
email_invalid
**Type** string
**Default** “Email address is not valid”
Error message when the email is invalid.
email_placeholder
**Type** string
**Default** “Email”
Copy for the email input.
mode?
**Type** “email” | “phone”
**Default** "email"
The mode to use for the input.
sendCode
**Type** (claims: Record<string, string>, code: string) => Promise<void>
Callback to send the pin code to the user.
The `claims` object contains the email or phone number of the user. You can send the code
using this.
```ts
async (claims, code) => {
// Send the code via the claim
}
```
================================================
FILE: www/src/content/docs/docs/ui/password.mdx
================================================
---
title: PasswordUI
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/ui/password.tsx
description: Reference doc for the `PasswordUI`.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
Configure the UI that's used by the Password provider.
```ts {1,7-12}
import { PasswordUI } from "@openauthjs/openauth/ui/password"
import { PasswordProvider } from "@openauthjs/openauth/provider/password"
export default issuer({
providers: {
password: PasswordAdapter(
PasswordUI({
copy: {
error_email_taken: "This email is already taken."
},
sendCode: (email, code) => console.log(email, code)
})
)
},
// ...
})
```
---
## Methods
### PasswordUI
```ts
PasswordUI(input)
```
#### Parameters
-
input [PasswordUIOptions](#passworduioptions)
Configure the UI.
**Returns** [PasswordConfig](/docs/provider/password#passwordconfig)
Creates a UI for the Password provider flow.
## PasswordUIOptions
-
Configure the password UI.
copy?
**Type** Object
Custom copy for the UI.
button_continue
**Type** string
**Default** “Continue”
Copy for the continue button.
change_prompt
**Type** string
**Default** “Forgot password?”
Copy for the forgot password link.
code_resend
**Type** string
**Default** “Resend code”
Copy for the resend code button.
code_return
**Type** string
**Default** “Back to”
Copy for the "Back to" link.
error_email_taken
**Type** string
**Default** “There is already an account with this email.”
Error message when email is already taken.
error_invalid_code
**Type** string
**Default** “Code is incorrect.”
Error message when the confirmation code is incorrect.
error_invalid_email
**Type** string
**Default** “Email is not valid.”
Error message when the email is invalid.
error_invalid_password
**Type** string
**Default** “Password is incorrect.”
Error message when the password is incorrect.
error_password_mismatch
**Type** string
**Default** “Passwords do not match.”
Error message when the passwords do not match.
error_validation_error
**Type** string
**Default** “Password does not meet requirements.”
Error message when the user enters a password that fails validation.
input_code
**Type** string
**Default** “Code”
Copy for the code input.
input_email
**Type** string
**Default** “Email”
Copy for the email input.
input_password
**Type** string
**Default** “Password”
Copy for the password input.
input_repeat
**Type** string
**Default** “Repeat password”
Copy for the repeat password input.
login
**Type** string
**Default** “Login”
Copy for the login button.
login_description
**Type** string
**Default** “Sign in with your email”
Description of the login page.
login_prompt
**Type** string
**Default** “Already have an account?”
Copy for the login link.
login_title
**Type** string
**Default** “Welcome to the app”
Title of the login page.
register
**Type** string
**Default** “Register”
Copy for the register button.
register_description
**Type** string
**Default** “Sign in with your email”
Description of the register page.
register_prompt
**Type** string
**Default** “Don't have an account?”
Copy for the register link.
register_title
**Type** string
**Default** “Welcome to the app”
Title of the register page.
sendCode
**Type** (email: string, code: string) => Promise<void>
Callback to send the confirmation pin code to the user.
```ts
{
sendCode: async (email, code) => {
// Send an email with the code
}
}
```
validatePassword?
**Type** [StandardSchema](https://github.com/standard-schema/standard-schema) | (password: string) => undefined | string | Promise<undefined | string>
Callback to validate the password on sign up and password reset.
```ts
{
validatePassword: (password) => {
return password.length < 8 ? "Password must be at least 8 characters" : undefined
}
}
```
================================================
FILE: www/src/content/docs/docs/ui/select.mdx
================================================
---
title: Select
editUrl: https://github.com/toolbeam/openauth/blob/master/packages/openauth/src/ui/select.tsx
description: Reference doc for the `Select` UI.
---
import { Segment, Section, NestedTitle, InlineSection } from 'toolbeam-docs-theme/components'
import { Tabs, TabItem } from '@astrojs/starlight/components'
The UI that's displayed when loading the root page of the OpenAuth server. You can configure
which providers should be displayed in the select UI.
```ts
import { Select } from "@openauthjs/openauth/ui/select"
export default issuer({
select: Select({
providers: {
github: {
hide: true
},
google: {
display: "Google"
}
}
})
// ...
})
```
---
## Methods
### Select
```ts
Select(props?)
```
#### Parameters
-
providers?
**Type** Record<string, Object>
An object with all the providers and their config; where the key is the provider name.
```ts
{
github: {
hide: true
},
google: {
display: "Google"
}
}
```
display?
**Type** string
The display name of the provider.
hide?
**Type** boolean
**Default** false
Whether to hide the provider from the select UI.
Use one of the built-in themes.
```ts
import { THEME_SST } from "@openauthjs/openauth/ui/theme"
export default issuer({
theme: THEME_SST,
// ...
})
```
Or define your own.
```ts
import type { Theme } from "@openauthjs/openauth/ui/theme"
const MY_THEME: Theme = {
title: "Acne",
radius: "none",
favicon: "https://www.example.com/favicon.svg",
// ...
}
export default issuer({
theme: MY_THEME,
// ...
})
```
---
## Themes
### THEME_OPENAUTH
**Default** “...”
Built-in default OpenAuth theme.
### THEME_SST
**Default** “...”
Built-in theme based on [SST](https://sst.dev).
### THEME_SUPABASE
**Default** “...”
Built-in theme based on [Supabase](https://supabase.com).
### THEME_TERMINAL
**Default** “...”
Built-in theme based on [Terminal](https://terminal.shop).
### THEME_VERCEL
**Default** “...”
Built-in theme based on [Vercel](https://vercel.com).
## ColorScheme
-
[dark](#colorscheme.dark) string
-
[light](#colorscheme.light) string
A type to define values for light and dark mode.
```ts
{
light: "#FFF",
dark: "#000"
}
```
dark
**Type** string
The value for dark mode.
light
**Type** string
The value for light mode.
## Theme
-
A type to define your custom theme.
background?
**Type** string | [ColorScheme](#colorscheme)
The background color of the theme.
Takes a color or both light and dark colors.
```ts
{
background: "#FFF"
}
```
css?
**Type** string
Custom CSS that's added to the page in a `