Compare commits

..

46 Commits

Author SHA1 Message Date
GitHub Actions
39e1a76e8f chore(release): bump package version(s) [skip ci] 2023-06-01 12:59:53 +00:00
Balázs Orbán
953ef9d04a chore: re-add pnpm caching
Related: #7332
2023-06-01 14:49:45 +02:00
Balázs Orbán
94f3031765 chore: allow manual release of any @auth/* package 2023-06-01 14:49:45 +02:00
Balázs Orbán
ad7bf07ddf chore: update lock file 2023-06-01 14:49:45 +02:00
Graham Charles
f30308ac30 docs: fix info card rendering in oauth-tutorial.mdx (#7662)
Info box is not being rendered; the raw `:::info` is displayed. Blind guess: it needs a blank line before it.
2023-06-01 14:49:45 +02:00
Tashrik Anam
6eaaeb15e9 docs: adapter card text color on hover when on dark mode (#7672) 2023-06-01 14:49:45 +02:00
Robert Soriano
8b3f0696a5 chore(playgrounds): Nuxt 3.5.1 (#7626)
* bump Nuxt to 3.5.1

* follow playground package names

* chore: update nuxt playground scripts

* fix: imports and types

* fix: more nuxt type imports

* fix: nuxt auth options types

* fix: nuxt client fetch types
2023-06-01 14:49:45 +02:00
Doron Sharon
c69a157832 chore: Add Descope as a 🥉 bronze financial sponsor (#7615)
Add Descope as a bronze sponsor
2023-06-01 14:49:45 +02:00
TATHAGATA ROY
60af446338 docs: Cypress.Cookies.defaults removed (#7574) 2023-06-01 14:49:45 +02:00
Nirmalya Ghosh
ce85444760 chore: Move next.config.js file into the correct directory (#7580)
fix: moves next config file into the correct directory
2023-06-01 14:49:45 +02:00
Balázs Orbán
142abe3eea feat: allow empty account mapper 2023-06-01 14:49:45 +02:00
Balázs Orbán
da211e6cbe chore: revert picture to image 2023-06-01 14:49:45 +02:00
Balázs Orbán
79ad6156ed feat: add update session to core (#7505)
* feat: add update session to core

Integrates #7056 into `@auth/core`

* resolve default user after jwt callback
2023-06-01 14:49:45 +02:00
Rémi Robichet
28f287d63e docs(example): update broken link (#7504)
Co-authored-by: Nico Domino <yo@ndo.dev>
2023-06-01 14:49:45 +02:00
Balázs Orbán
1ab77d0e11 chore: move build to root 2023-06-01 14:49:45 +02:00
Balázs Orbán
787c1ff7d0 chore: add build to manual publish 2023-06-01 14:49:45 +02:00
Balázs Orbán
208b3b4a43 chore: reduce breaking changes on Account mapping
Reverts some changes on #7369 so DB migration won't be needed
2023-06-01 14:49:45 +02:00
Balázs Orbán
c4f6330f70 chore: tweak manual release version 2023-06-01 14:49:45 +02:00
Balázs Orbán
44127068e1 chore: tweaks 2023-06-01 14:49:45 +02:00
Balázs Orbán
9e3f1aacf7 chore: tweak 2023-06-01 14:49:45 +02:00
Balázs Orbán
83051c6862 chore: skip test for manual release 2023-06-01 14:49:45 +02:00
Balázs Orbán
f1acab67e6 chore: separate manual release job 2023-06-01 14:49:45 +02:00
Balázs Orbán
6a31ed3216 chore: support release any package as experimental 2023-06-01 14:49:45 +02:00
Balázs Orbán
0998fc0b98 chore: use @ts-ignore 2023-06-01 14:49:45 +02:00
Balázs Orbán
bd20d750c2 fix(docs): remove extra heading
Fixes #7426
2023-06-01 14:49:45 +02:00
Balázs Orbán
8e29b4df0c fix: allow handling OAuth callback error response
related #7407
2023-06-01 14:49:45 +02:00
Balázs Orbán
9632a56d45 chore: type fixes 2023-06-01 14:49:45 +02:00
Balázs Orbán
12161b9613 fix: loosen profile types 2023-06-01 14:49:45 +02:00
Balázs Orbán
a3b5276a5a chore: improve errors, add more docs (#7415)
* JWT Token -> JWT

* document some errors

* improve errors, docs
2023-06-01 14:49:45 +02:00
Balázs Orbán
7c1078b9a9 feat(adapters): add Account mapping before database write (#7369)
* feat: map Account before saving to database

* document `acconut()`, explain default behaviour

* generate `expires_at` based on `expires_in`

Fixes #6538

* rename

* strip undefined on `defaultProfile`

* don't forward defaults to account callback

* improve internal namings, types, docs
2023-06-01 14:49:45 +02:00
Victor
37d3461155 docs: fix default maxAge formula (#7406) 2023-06-01 14:49:40 +02:00
Balázs Orbán
6111662df7 docs: Update creating-a-database-adapter.md 2023-04-30 09:52:47 +02:00
Zack Reneau-Wedeen
5da6549c48 chore(docs): update xata docs link (#7397)
Update link to a working page (Workspaces API reference)

Co-authored-by: Nico Domino <yo@ndo.dev>
2023-04-28 22:26:30 +02:00
GitHub Actions
1ca87809d6 chore(release): bump package version(s) [skip ci] 2023-04-28 15:09:12 +00:00
Balázs Orbán
7f6967fc3c chore: update lock file 2023-04-28 16:55:31 +02:00
Dawid Weltrowski-Knopik
2313ef63e0 fix(adapters): allow neo4j-driver@5 as a peer dependency (#7392)
* upgrade neo4j-driver

* Correcting depdendency to also allow for ^4.0.0 and pnpm install

---------

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-04-28 15:54:48 +01:00
Balázs Orbán
523fcbab71 fix(docs): document Callback URL for every provider 2023-04-28 16:52:10 +02:00
Balázs Orbán
83d8b447db chore: clean up Next.js example 2023-04-28 09:50:44 +02:00
Balázs Orbán
ddffa57d00 docs: fix syntax error 2023-04-27 10:06:12 +01:00
peterhirn
807d5d7920 fix(ts): support moduleResolution node16 and nodenext (#7351) (#7374) 2023-04-27 10:03:25 +01:00
RubenSmn
0f0dd9228a docs: fix code snippet being smushed on mobile (#7197) 2023-04-26 13:06:11 +01:00
Jack Oats
b087fdb817 docs: resize icons on screen size change (#7309)
* Update size of icons on load && on resize event

Added 'use client' since window object would only be defined on client side ( and using hooks );
Thought it'd be a cool feature to add :)

* Apply suggestions from code review

---------

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-04-26 13:00:15 +01:00
Lluis Agusti
443bfd6c32 fix(docs): move provider docs to source code (#7275)
* chore: wip

* chore: wip

* zoom, zoho, ZITADEL

* add logos for Zoom, zoho, zitadel

* add the rest of the providers

* add logos

* revert typo

* move icons to docs

---------

Co-authored-by: Thang Vu <hi@thvu.dev>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-04-24 09:21:24 +01:00
Balázs Orbán
7c44d916ed chore: fix Next.js example sync 2023-04-23 08:14:52 +01:00
John Abdou
b489fef2e2 docs: fix link (#7334)
* Fixed broken link to auth.js core documentation

* Update README.md

---------

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-04-23 09:22:14 +03:00
Balázs Orbán
98add24526 chore: bump react types 2023-04-21 12:25:48 +02:00
167 changed files with 8512 additions and 2132 deletions

9
.github/sync.yml vendored
View File

@@ -1,5 +1,3 @@
# Note that nextauthjs/next-auth-example syncs from the v4 branch
nextauthjs/sveltekit-auth-example:
- source: apps/examples/sveltekit
dest: .
@@ -20,3 +18,10 @@ nextauthjs/next-auth-gatsby-example:
deleteOrphaned: true
- .github/FUNDING.yml
- LICENSE
nextauthjs/next-auth-example:
- source: apps/examples/nextjs
dest: .
deleteOrphaned: true
- .github/FUNDING.yml
- LICENSE

View File

@@ -5,14 +5,15 @@ const core = require("@actions/core")
try {
const packageJSONPath = path.join(
process.cwd(),
"packages/next-auth/package.json"
`packages/${process.env.PACKAGE_PATH || "next-auth"}/package.json`
)
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"))
const sha8 = process.env.GITHUB_SHA.substring(0, 8)
const prNumber = process.env.PR_NUMBER
const packageVersion = `0.0.0-pr.${prNumber}.${sha8}`
const prefix = "0.0.0-"
const pr = process.env.PR_NUMBER
const source = pr ? `pr.${pr}` : "manual"
const packageVersion = `${prefix}${source}.${sha8}`
packageJSON.version = packageVersion
core.setOutput("version", packageVersion)
fs.writeFileSync(packageJSONPath, JSON.stringify(packageJSON))

View File

@@ -8,6 +8,54 @@ on:
- next
- 3.x
pull_request:
# TODO: Support latest releases
workflow_dispatch:
inputs:
name:
type: choice
description: Package name (npm)
options:
- "@auth/core"
- "@auth/nextjs"
- "@auth/dgraph-adapter"
- "@auth/drizzle-adapter"
- "@auth/dynamodb-adapter"
- "@auth/fauna-adapter"
- "@auth/firebase-adapter"
- "@auth/mikro-orm-adapter"
- "@auth/mongodb-adapter"
- "@auth/neo4j-adapter"
- "@auth/pouchdb-adapter"
- "@auth/prisma-adapter"
- "@auth/sequelize-adapter"
- "@auth/supabase-adapter"
- "@auth/typeorm-legacy-adapter"
- "@auth/upstash-redis-adapter"
- "@auth/xata-adapter"
- "next-auth"
# TODO: Infer from package name
path:
type: choice
description: Directory name (packages/*)
options:
- "core"
- "frameworks-nextjs"
- "adapter-dgraph"
- "adapter-drizzle"
- "adapter-dynamodb"
- "adapter-fauna"
- "adapter-firebase"
- "adapter-mikro-orm"
- "adapter-mongodb"
- "adapter-neo4j"
- "adapter-pouchdb"
- "adapter-prisma"
- "adapter-sequelize"
- "adapter-supabase"
- "adapter-typeorm-legacy"
- "adapter-upstash-redis"
- "adapter-xata"
- "next-auth"
jobs:
test:
@@ -24,6 +72,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Run tests
@@ -73,6 +122,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Publish to npm and GitHub
@@ -97,6 +147,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Determine version
@@ -122,3 +173,36 @@ jobs:
env:
VERSION: ${{ steps.determine-version.outputs.version }}
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
release-manual:
name: Publish manually
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
steps:
- name: Init
uses: actions/checkout@v3
- name: Install pnpm
uses: pnpm/action-setup@v2.2.4
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Determine version
uses: ./.github/version-pr
id: determine-version
env:
PACKAGE_PATH: ${{ github.event.inputs.path }}
- name: Publish to npm
run: |
pnpm build
cd packages/$PACKAGE_PATH
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
pnpm publish --no-git-checks --access public --tag experimental
echo "🎉 Experimental release published 📦️ on npm: https://npmjs.com/package/${{ github.event.inputs.name }}/v/${{ env.VERSION }}"
echo "Install via: pnpm add ${{ github.event.inputs.name }}@${{ env.VERSION }}"
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
PACKAGE_PATH: ${{ github.event.inputs.path }}
VERSION: ${{ steps.determine-version.outputs.version }}

View File

@@ -29,7 +29,7 @@
},
"devDependencies": {
"@types/jsonwebtoken": "^8.5.5",
"@types/react": "^18.0.15",
"@types/react": "^18.0.37",
"@types/react-dom": "^18.0.6",
"fake-smtp-server": "^0.8.0",
"pg": "^8.7.3",

View File

@@ -31,7 +31,7 @@
"devDependencies": {
"@playwright/test": "1.29.2",
"@types/jsonwebtoken": "^8.5.5",
"@types/react": "^18.0.15",
"@types/react": "18.0.37",
"@types/react-dom": "^18.0.6",
"dotenv": "^16.0.3",
"fake-smtp-server": "^0.8.0",

View File

@@ -1,10 +1,6 @@
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET= # Linux: `openssl rand -hex 32` or go to https://generate-secret.now.sh/32
APPLE_ID=
APPLE_TEAM_ID=
APPLE_PRIVATE_KEY=
APPLE_KEY_ID=
AUTH0_ID=
AUTH0_SECRET=
@@ -21,8 +17,3 @@ GOOGLE_SECRET=
TWITTER_ID=
TWITTER_SECRET=
EMAIL_SERVER=smtp://username:password@smtp.example.com:587
EMAIL_FROM=NextAuth <noreply@example.com>
DATABASE_URL=sqlite://localhost/:memory:?synchronize=true

View File

@@ -0,0 +1,4 @@
/** @type {import("next").NextConfig} */
module.exports = {
reactStrictMode: true,
}

View File

@@ -20,13 +20,12 @@
"dependencies": {
"next": "latest",
"next-auth": "latest",
"nodemailer": "^6",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/node": "^17",
"@types/react": "^18.0.15",
"typescript": "^4"
"@types/node": "^18.16.2",
"@types/react": "^18.2.0",
"typescript": "^5.0.4"
}
}

View File

@@ -7,7 +7,7 @@ export default function Page() {
<p>Only admin users can see this page.</p>
<p>
To learn more about the NextAuth middleware see&nbsp;
<a href="https://docs-git-misc-docs-nextauthjs.vercel.app/configuration/nextjs#middleware">
<a href="https://next-auth.js.org/configuration/nextjs#middleware">
the docs
</a>
.

View File

@@ -4,31 +4,17 @@ import FacebookProvider from "next-auth/providers/facebook"
import GithubProvider from "next-auth/providers/github"
import TwitterProvider from "next-auth/providers/twitter"
import Auth0Provider from "next-auth/providers/auth0"
// import AppleProvider from "next-auth/providers/apple"
// import EmailProvider from "next-auth/providers/email"
// For more information on each option (and a full list of options) go to
// https://next-auth.js.org/configuration/options
export const authOptions: NextAuthOptions = {
// https://next-auth.js.org/configuration/providers/oauth
providers: [
/* EmailProvider({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
// Temporarily removing the Apple provider from the demo site as the
// callback URL for it needs updating due to Vercel changing domains
Providers.Apple({
clientId: process.env.APPLE_ID,
clientSecret: {
appleId: process.env.APPLE_ID,
teamId: process.env.APPLE_TEAM_ID,
privateKey: process.env.APPLE_PRIVATE_KEY,
keyId: process.env.APPLE_KEY_ID,
},
Auth0Provider({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
issuer: process.env.AUTH0_ISSUER,
}),
*/
FacebookProvider({
clientId: process.env.FACEBOOK_ID,
clientSecret: process.env.FACEBOOK_SECRET,
@@ -44,16 +30,9 @@ export const authOptions: NextAuthOptions = {
TwitterProvider({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET,
}),
Auth0Provider({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
issuer: process.env.AUTH0_ISSUER,
version: "2.0",
}),
],
theme: {
colorScheme: "light",
},
callbacks: {
async jwt({ token }) {
token.userRole = "admin"

View File

@@ -1,5 +1,5 @@
// This is an example of to protect an API route
import { unstable_getServerSession } from "next-auth/next"
import { getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
import type { NextApiRequest, NextApiResponse } from "next"
@@ -8,7 +8,7 @@ export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await unstable_getServerSession(req, res, authOptions)
const session = await getServerSession(req, res, authOptions)
if (session) {
return res.send({

View File

@@ -1,5 +1,5 @@
// This is an example of how to access a session from an API route
import { unstable_getServerSession } from "next-auth"
import { getServerSession } from "next-auth"
import { authOptions } from "../auth/[...nextauth]"
import type { NextApiRequest, NextApiResponse } from "next"
@@ -8,6 +8,6 @@ export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await unstable_getServerSession(req, res, authOptions)
const session = await getServerSession(req, res, authOptions)
res.send(JSON.stringify(session, null, 2))
}

View File

@@ -13,11 +13,11 @@ export default function ServerSidePage() {
<Layout>
<h1>Server Side Rendering</h1>
<p>
This page uses the <strong>unstable_getServerSession()</strong> method
in <strong>getServerSideProps()</strong>.
This page uses the <strong>getServerSession()</strong> method in{" "}
<strong>getServerSideProps()</strong>.
</p>
<p>
Using <strong>unstable_getServerSession()</strong> in{" "}
Using <strong>getServerSession()</strong> in{" "}
<strong>getServerSideProps()</strong> is the recommended approach if you
need to support Server Side Rendering with authentication.
</p>
@@ -38,11 +38,7 @@ export default function ServerSidePage() {
export async function getServerSideProps(context: GetServerSidePropsContext) {
return {
props: {
session: await getServerSession(
context.req,
context.res,
authOptions
),
session: await getServerSession(context.req, context.res, authOptions),
},
}
}

View File

@@ -1,4 +1,4 @@
import { Session } from "@auth/core"
import { Session } from "@auth/core/types"
export default function useSession() {
return useState<Session | null>("session", () => null)

View File

@@ -43,7 +43,7 @@ export async function signIn<
// TODO: Handle custom base path
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
const { csrfToken } = await $fetch("/api/auth/csrf")
const { csrfToken } = await $fetch<{ csrfToken: string }>("/api/auth/csrf")
console.log(_signInUrl)

View File

@@ -1,13 +1,14 @@
import { AuthHandler, AuthOptions, Session } from "@auth/core"
import { AuthConfig, Session } from "@auth/core/types"
import { Auth } from "@auth/core"
import { fromNodeMiddleware, H3Event } from "h3"
import getURL from "requrl"
import { createMiddleware } from "@hattip/adapter-node"
export function NuxtAuthHandler(options: AuthOptions) {
export function NuxtAuthHandler(options: AuthConfig) {
async function handler(ctx: { request: Request }) {
options.trustHost ??= true
return AuthHandler(ctx.request, options)
return Auth(ctx.request, options)
}
const middleware = createMiddleware(handler)
@@ -17,7 +18,7 @@ export function NuxtAuthHandler(options: AuthOptions) {
export async function getSession(
event: H3Event,
options: AuthOptions
options: AuthConfig
): Promise<Session | null> {
options.trustHost ??= true
@@ -30,7 +31,7 @@ export async function getSession(
nodeHeaders.append(key, headers[key] as any)
})
const response = await AuthHandler(
const response = await Auth(
new Request(url, { headers: nodeHeaders }),
options
)

View File

@@ -1,21 +1,22 @@
{
"name": "playground-nuxt",
"name": "next-auth-nuxt",
"private": true,
"scripts": {
"build": "nuxt prepare && nuxt build",
"dev": "nuxt prepare && export NODE_OPTIONS='--no-experimental-fetch' && nuxt dev",
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview"
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@nuxt/eslint-config": "^0.1.1",
"eslint": "^8.29.0",
"h3": "1.0.2",
"nuxt": "3.0.0"
"h3": "1.6.6",
"nuxt": "3.5.1"
},
"dependencies": {
"@auth/core": "workspace:*",
"@hattip/adapter-node": "^0.0.22",
"@hattip/adapter-node": "^0.0.34",
"requrl": "^3.0.2"
}
}

View File

@@ -1,4 +1,4 @@
import { Session } from "@auth/core"
import { Session } from "@auth/core/types"
export default defineNuxtPlugin(async () => {
const session = useSession()

View File

@@ -1,10 +1,10 @@
import { NuxtAuthHandler } from "@/lib/auth/server"
import GithubProvider from "@auth/core/providers/github"
import type { AuthOptions } from "@auth/core"
import type { AuthConfig } from "@auth/core"
const runtimeConfig = useRuntimeConfig()
export const authOptions: AuthOptions = {
export const authOptions = {
secret: runtimeConfig.secret,
providers: [
GithubProvider({
@@ -12,6 +12,6 @@ export const authOptions: AuthOptions = {
clientSecret: runtimeConfig.github.clientSecret,
}),
],
}
} as AuthConfig
export default NuxtAuthHandler(authOptions)

View File

@@ -269,7 +269,7 @@ Ultimately if your request is not accepted or is not actively in development, yo
</summary>
<p>
Auth.js by default uses JSON Web Tokens for saving the user's session. However, if you use a [database adapter](/guides/adapters/using-a-database-adapter), the database will be used to persist the user's session. You can force the usage of JWT when using a database [through the configuration options](/reference/configuration/auth-config#session). Since v4 all our JWT tokens are now encrypted by default with A256GCM.
Auth.js by default uses JSON Web Tokens for saving the user's session. However, if you use a [database adapter](/guides/adapters/using-a-database-adapter), the database will be used to persist the user's session. You can force the usage of JWT when using a database [through the configuration options](/reference/configuration/auth-config#session). Since v4 all our JWTs are now encrypted by default with A256GCM.
</p>
</details>

View File

@@ -243,10 +243,13 @@ http://localhost:5173/auth/callback/github
TODO Core
</TabItem>
</Tabs>
:::info
The last part of the URL, `[provider]`, is the ID of the provider you're using. In this case, we're using GitHub, so it's `github`. If you're using Google, it'll be `google`, etc... We keep track of the provider IDs internally.
The same id is used in the `signIn()` method we saw earlier.
:::
To register, tap on "Register application" button.
The next screen shows all the configurations for your newly created OAuth app. For now, we need two things from it - the **Client ID** and **Client Secret**:

View File

@@ -6,7 +6,7 @@ Using a custom adapter you can connect to any database back-end or even several
## How to create an adapter
For more information about the data these methods need to manage see [models](/reference/adapters/models).
For more information about the data these methods need to manage see [models](/reference/adapters#models).
_See the code below for practical example._

View File

@@ -29,7 +29,7 @@ Sent when the user signs out.
The message object will contain one of these depending on if you use JWT or database persisted sessions:
- `token`: The JWT token for this session.
- `token`: The JWT for this session.
- `session`: The session object from your adapter that is being ended
### createUser
@@ -60,5 +60,5 @@ Sent at the end of a request for the current session.
The message object will contain one of these depending on if you use JWT or database persisted sessions:
- `token`: The JWT token for this session.
- `token`: The JWT for this session.
- `session`: The session object from your adapter.

View File

@@ -110,10 +110,6 @@ describe("Login page", () => {
secure: cookie.secure,
})
Cypress.Cookies.defaults({
preserve: cookieName,
})
// remove the two lines below if you need to stay logged in
// for your remaining tests
cy.visit("/api/auth/signout")

View File

@@ -2,7 +2,7 @@
title: Overview
---
Using a Auth.js / NextAuth.js adapter you can connect to any database service or even several different services at the same time. The following listed official adapters are created and maintained by the community:
Using an Auth.js / NextAuth.js adapter you can connect to any database service or even several different services at the same time. The following listed official adapters are created and maintained by the community:
<div class="adapter-card-list">
<a href="/reference/adapter/dgraph" class="adapter-card">
@@ -71,7 +71,7 @@ If you don't find an adapter for the database or service you use, you can always
## Models
Auth.js can be used with any database. Models tell you what structures Auth.js expects from your database. Models will vary slightly depending on which adapter you use, but in general, will look something like this. Each adapter's model/schema will be slightly adapted for its needs, but will look very much like this schema below:
Auth.js can be used with any database. Models tell you what structures Auth.js expects from your database. Models will vary slightly depending on which adapter you use, but in general, will look something like this:
```mermaid
erDiagram
@@ -103,8 +103,6 @@ erDiagram
string scope
string id_token
string session_state
string oauth_token_secret
string oauth_token
}
VerificationToken {
string identifier
@@ -113,10 +111,10 @@ erDiagram
}
```
More information about each Model / Table can be found below.
More information about each Model/Table can be found below.
:::note
You can [create your own adapter](/guides/adapters/creating-a-database-adapter) if you want to use Auth.js with a database that is not supported out of the box, or you have to change fields on any of the models.
You can [create your adapter](/guides/adapters/creating-a-database-adapter) if you want to use Auth.js with a database that is not supported out of the box, or you have to change fields on any of the models.
:::
---
@@ -125,30 +123,31 @@ You can [create your own adapter](/guides/adapters/creating-a-database-adapter)
The User model is for information such as the user's name and email address.
Email address is optional, but if one is specified for a User then it must be unique.
Email address is optional, but if one is specified for a User, then it must be unique.
:::note
If a user first signs in with OAuth then their email address is automatically populated using the one from their OAuth profile, if the OAuth provider returns one.
If a user first signs in with an OAuth provider, then their email address is automatically populated using the one from their OAuth profile if the OAuth provider returns one.
This provides a way to contact users and for users to maintain access to their account and sign in using email in the event they are unable to sign in with the OAuth provider in future (if the [Email Provider](/getting-started/email-tutorial) is configured).
This provides a way to contact users and for users to maintain access to their account and sign in using email in the event they are unable to sign in with the OAuth provider in the future (if the [Email Provider](/reference/core/providers_email) is configured).
:::
User creation in the database is automatic, and happens when the user is logging in for the first time with a provider. The default data saved is `id`, `name`, `email` and `image`. You can add more profile data by returning extra fields in your [OAuth provider](/guides/providers/custom-provider)'s [`profile()`](/reference/core/providers#profile) callback.
User creation in the database is automatic and happens when the user is logging in for the first time with a provider.
If the first sign-in is via the [OAuth Provider](/reference/core/providers_oauth), the default data saved is `id`, `name`, `email` and `image`. You can add more profile data by returning extra fields in your [OAuth provider](/guides/providers/custom-provider)'s [`profile()`](/reference/core/providers#profile) callback.
If the first sign-in is via the [Email Provider](/reference/core/providers_email), then the saved user will have `id`, `email`, `emailVerified`, where `emailVerified` is the timestamp of when the user was created.
### Account
The Account model is for information about OAuth accounts associated with a User. It will usually contain `access_token`, `id_token` and other OAuth specific data. [`TokenSet`](https://github.com/panva/node-openid-client/blob/main/docs/README.md#new-tokensetinput) from `openid-client` might give you an idea of all the fields.
:::note
In case of an OAuth 1.0 provider (like Twitter), you will have to look for `oauth_token` and `oauth_token_secret` string fields. GitHub also has an extra `refresh_token_expires_in` integer field. You have to make sure that your database schema includes these fields.
:::
The Account model is for information about OAuth accounts associated with a User
A single User can have multiple Accounts, but each Account can only have one User.
Linking Accounts to Users happen automatically, only when they have the same e-mail address, and the user is currently signed in. Check the [FAQ](/concepts/faq#security) for more information why this is a requirement.
Account creation in the database is automatic and happens when the user is logging in for the first time with a provider, or the [`Adapter.linkAccount`](/reference/core/adapters#linkaccount) method is invoked. The default data saved is `access_token`, `expires_at`, `refresh_token`, `id_token`, `token_type`, `scope` and `session_state`. You can save other fields or remove the ones you don't need by returning them in the [OAuth provider](/guides/providers/custom-provider)'s [`account()`](/reference/core/providers#account) callback.
Linking Accounts to Users happen automatically, only when they have the same e-mail address, and the user is currently signed in. Check the [FAQ](/concepts/faq#security) for more information on why this is a requirement.
:::tip
You can manually unlink accounts, if your adapter implements the `unlinkAccount` method. Make sure to take all the necessary security steps to avoid data loss.
You can manually unlink accounts if your adapter implements the `unlinkAccount` method. Make sure to take all the necessary security steps to avoid data loss.
:::
:::note
@@ -162,7 +161,7 @@ The Session model is used for database sessions. It is not used if JSON Web Toke
A single User can have multiple Sessions, each Session can only have one User.
:::tip
When a Session is read, we check if it's `expires` field indicates an invalid session, and delete it from the database. You can also do this clean-up periodically in the background to avoid our extra delete call to the database during an active session retrieval. This might result in a slight performance increase in a few cases.
When a Session is read, we check if its `expires` field indicates an invalid session, and delete it from the database. You can also do this clean-up periodically in the background to avoid our extra delete call to the database during an active session retrieval. This might result in a slight performance increase in a few cases.
:::
### Verification Token
@@ -171,7 +170,7 @@ The Verification Token model is used to store tokens for passwordless sign in.
A single User can have multiple open Verification Tokens (e.g. to sign in to different devices).
It has been designed to be extendable for other verification purposes in the future (e.g. 2FA / short codes).
It has been designed to be extendable for other verification purposes in the future (e.g. 2FA / magic codes, etc.).
:::note
Auth.js makes sure that every token is usable only once, and by default has a short (1 day, can be configured by [`maxAge`](/guides/providers/email)) lifetime. If your user did not manage to finish the sign-in flow in time, they will have to start the sign-in process again.
@@ -183,8 +182,7 @@ Due to users forgetting or failing at the sign-in flow, you might end up with un
## RDBMS Naming Convention
Auth.js / NextAuth.js uses `camelCase` for its own database rows, while respecting the conventional `snake_case` formatting for OAuth related values. If mixed casing is an issue for you, most adapters have a dedicated section on how to use a single naming convention.
Auth.js / NextAuth.js uses `camelCase` for its database rows while respecting the conventional `snake_case` formatting for OAuth-related values. If the mixed casing is an issue for you, most adapters have a dedicated documentation section on how to force a casing convention.
## TypeScript

View File

@@ -7,7 +7,7 @@ const path = require("path")
const coreSrc = "../packages/core/src"
const providers = fs
.readdirSync(path.join(__dirname, coreSrc, "/providers"))
.filter((file) => file.endsWith(".ts") && !file.startsWith("oauth"))
.filter((file) => file.endsWith(".ts"))
.map((p) => `${coreSrc}/providers/${p}`)
const typedocConfig = require("./typedoc.json")

View File

@@ -1,22 +1,20 @@
Add $1 login to your page.
## Example
@example
```js
import Auth from "@auth/core"
import { $1 } from "@auth/core/providers/$2"
import $1 from "@auth/core/providers/$2"
const request = new Request("https://example.com")
const response = await AuthHandler(request, {
providers: [$1({ clientId: "", clientSecret: "" })],
const request = new Request(origin)
const response = await Auth(request, {
providers: [$1({ clientId: $3CLIENT_ID, clientSecret: $3CLIENT_SECRET })],
})
```
## Resources
@see [Link 1](https://example.com)
- [$1 OAuth documentation](https://example.com)
## Notes

View File

@@ -1,29 +1,25 @@
Add $1 login to your page.
## Example
@example
```ts
import { Auth } from "@auth/core"
```js
import Auth from "@auth/core"
import $1 from "@auth/core/providers/$2"
const request = new Request("https://example.com")
const response = await AuthHandler(request, {
providers: [$1({ clientId: "", clientSecret: "" })],
const request = new Request(origin)
const response = await Auth(request, {
providers: [$1({ clientId: $3CLIENT_ID, clientSecret: $3CLIENT_SECRET })],
})
```
---
## Resources
- [Link 1](https://example.com)
---
- [$1 OAuth documentation](https://example.com)
## Notes
By default, Auth.js assumes that the $1 provider is
based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.
:::tip

View File

@@ -19,23 +19,29 @@ const icons = [
"/img/providers/twitter.svg",
]
export default React.memo(function ProviderMarquee() {
let scale = 0.4
function changeScale() {
if (typeof window !== "undefined") {
const width = window.outerWidth
if (width > 800) {
scale = 0.6
}
if (width > 1100) {
scale = 0.7
}
if (width > 1400) {
scale = 0.8
}
if (width > 800) return 0.6
else if (width > 1100) return 0.7
else if (width > 1400) return 0.8
}
}
export default React.memo(function ProviderMarquee() {
// Get initial scale on load
const [scale, setScale] = React.useState(changeScale)
React.useEffect(() => {
// Account for window size change
function handleEvent() {
setScale(changeScale)
}
window.addEventListener("resize", handleEvent)
return () => window.removeEventListener("resize", handleEvent)
}, [])
return (
<div className={styles.fullWidth}>

View File

@@ -29,6 +29,7 @@ html[data-theme="dark"] .adapter-card {
color: #f5f5f5;
}
html[data-theme="dark"] .adapter-card:hover,
.adapter-card:hover {
text-decoration: none;
color: black;

View File

@@ -124,6 +124,9 @@ html[data-theme="dark"] hr {
font-size: 1rem;
font-weight: 700;
width: 100%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.home-main .code .code-heading span {

12
docs/static/img/providers/42-school.svg vendored Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="Calque_1" sodipodi:docname="42_logo.svg" inkscape:version="0.48.2 r9819" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 -200 960 960"
enable-background="new 0 -200 960 960" xml:space="preserve">
<polygon id="polygon5" points="32,412.6 362.1,412.6 362.1,578 526.8,578 526.8,279.1 197.3,279.1 526.8,-51.1 362.1,-51.1
32,279.1 "/>
<polygon id="polygon7" points="597.9,114.2 762.7,-51.1 597.9,-51.1 "/>
<polygon id="polygon9" points="762.7,114.2 597.9,279.1 597.9,443.9 762.7,443.9 762.7,279.1 928,114.2 928,-51.1 762.7,-51.1 "/>
<polygon id="polygon11" points="928,279.1 762.7,443.9 928,443.9 "/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1 @@
<svg height="447" viewBox="0 0 1101.64 196.79" width="2500" xmlns="http://www.w3.org/2000/svg"><path d="m222.34 54.94c-40.02 0-71.29 30.38-71.29 71.05s30.48 70.79 71.29 70.79 71.82-30.64 71.82-71.05c0-40.15-30.48-70.79-71.82-70.79zm.27 112.53c-22.79 0-39.49-17.7-39.49-41.47 0-24.04 16.43-41.73 39.22-41.73 23.06 0 39.75 17.96 39.75 41.73s-16.69 41.47-39.48 41.47zm80.29-81.62h19.88v108.3h31.8v-136.57h-51.68zm-231.88-1.59c16.7 0 29.95 10.3 34.98 25.62h33.66c-6.1-32.75-33.13-54.94-68.37-54.94-40.02 0-71.29 30.38-71.29 71.06s30.48 70.79 71.29 70.79c34.45 0 62.01-22.19 68.11-55.21h-33.4c-4.77 15.32-18.02 25.89-34.72 25.89-23.06 0-39.22-17.7-39.22-41.47.01-24.04 15.91-41.74 38.96-41.74zm836.1 28.53-23.32-3.43c-11.13-1.58-19.08-5.28-19.08-14 0-9.51 10.34-14.26 24.38-14.26 15.37 0 25.18 6.6 27.3 17.43h30.74c-3.45-27.47-24.65-43.58-57.24-43.58-33.66 0-55.92 17.17-55.92 41.47 0 23.24 14.58 36.72 43.99 40.94l23.32 3.43c11.4 1.58 17.76 6.08 17.76 14.53 0 10.83-11.13 15.32-26.5 15.32-18.82 0-29.42-7.66-31.01-19.28h-31.27c2.92 26.68 23.85 45.43 62.01 45.43 34.72 0 57.77-15.85 57.77-43.06 0-24.3-16.69-36.98-42.93-40.94zm-568.44-111.47c-11.66 0-20.41 8.45-20.41 20.07s8.74 20.07 20.41 20.07c11.66 0 20.41-8.45 20.41-20.07s-8.75-20.07-20.41-20.07zm466.68 103.02c0-29.58-18.02-49.39-56.18-49.39-36.04 0-56.18 18.23-60.16 46.23h31.54c1.59-10.83 10.07-19.81 28.09-19.81 16.17 0 24.12 7.13 24.12 15.85 0 11.36-14.58 14.26-32.6 16.11-24.38 2.64-54.59 11.09-54.59 42.79 0 24.57 18.29 40.41 47.44 40.41 22.79 0 37.1-9.51 44.26-24.57 1.06 13.47 11.13 22.19 25.18 22.19h18.55v-28.26h-15.64v-61.55zm-31.27 34.34c0 18.23-15.9 31.7-35.25 31.7-11.93 0-22-5.02-22-15.58 0-13.47 16.17-17.17 31.01-18.75 14.31-1.32 22.26-4.49 26.24-10.57zm-168.81-83.74c-17.76 0-32.6 7.4-43.2 19.81v-74.75h-31.8v194.15h31.27v-17.96c10.6 12.94 25.71 20.6 43.73 20.6 38.16 0 67.05-30.11 67.05-70.79s-29.42-71.06-67.05-71.06zm-4.77 112.53c-22.79 0-39.49-17.7-39.49-41.47s16.96-41.73 39.75-41.73c23.06 0 39.22 17.7 39.22 41.73 0 23.77-16.69 41.47-39.48 41.47zm-146.29-112.53c-20.67 0-34.19 8.45-42.14 20.34v-17.7h-31.54v136.56h31.8v-74.22c0-20.87 13.25-35.66 32.86-35.66 18.29 0 29.68 12.94 29.68 31.7v78.19h31.8v-80.56c.01-34.35-17.74-58.65-52.46-58.65zm647.42 66.57c0-39.09-28.62-66.56-67.05-66.56-40.81 0-70.76 30.64-70.76 71.05 0 42.53 32.07 70.79 71.29 70.79 33.13 0 59.1-19.55 65.72-47.28h-33.13c-4.77 12.15-16.43 19.02-32.07 19.02-20.41 0-35.78-12.68-39.22-34.87h105.21v-12.15zm-103.36-10.57c5.04-19.02 19.35-28.26 35.78-28.26 18.02 0 31.8 10.3 34.98 28.26z" fill="#0052ff"/></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

1
docs/static/img/providers/dropbox.svg vendored Normal file
View File

@@ -0,0 +1 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42.4 39.5" width="2500" height="2329"><style>.st0{fill:#0062ff}</style><path class="st0" d="M10.6 1.7L0 8.5l10.6 6.7 10.6-6.7zm21.2 0L21.2 8.5l10.6 6.7 10.6-6.7zM0 22l10.6 6.8L21.2 22l-10.6-6.8zm31.8-6.8L21.2 22l10.6 6.8L42.4 22zM10.6 31l10.6 6.8L31.8 31l-10.6-6.7z"/></svg>

After

Width:  |  Height:  |  Size: 340 B

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1280 1280" style="enable-background:new 0 0 1280 1280;" xml:space="preserve">
<style type="text/css">
.st0{fill:#252122;}
.st1{fill:#F6971D;}
</style>
<path class="st0" d="M639.7,17C293.2,17,12.2,297.9,12.2,644.5S293.2,1272,639.7,1272c346.6,0,627.5-280.9,627.5-627.5
S986.3,17,639.7,17z M639.7,1203.4c-308.7,0-558.9-250.2-558.9-558.9c0-308.7,250.2-558.9,558.9-558.9
c308.7,0,558.9,250.2,558.9,558.9C1198.6,953.2,948.4,1203.4,639.7,1203.4z"/>
<g>
<path class="st1" d="M573,917.4c0.1-4,0.3-7.9,0.3-11.9c0.1-205.8,0.2-411.7,0.3-617.5c0-3.6,0-7.2,0-11.7
c50.9-25.9,101.8-51.8,153.9-78.3c0,252.5,0,503.3,0,754.7c-37.6,22.7-75.4,45.5-113.3,68.1c-13.5,8.1-27.3,15.8-40.9,23.7
C573.2,1002.2,573.1,959.8,573,917.4z"/>
<path class="st0" d="M573,917.4c0.1,42.4,0.2,84.8,0.4,127.1c-34.5-4.5-67.5-14.6-99.5-27.6c-68.3-27.6-126.9-68.3-170-129
c-75.6-106.7-69.3-237.9,15.9-337.3c59-68.8,135.1-109.6,222.2-134.6c0,43.7,0,86.3,0,129.9c-8.6,3.7-18,7.5-27.2,11.8
c-39.9,18.6-75.4,43.1-103.4,77.7c-47.7,58.9-47.7,128.3,0.2,187.2c36.9,45.4,85.9,71.7,140.6,89.1
C558.9,913.9,566,915.5,573,917.4z"/>
<path class="st0" d="M923.1,448.7c7.9-15.2,15.5-29.7,23.7-45.5c37.2,58.7,73.8,116.3,111.2,175.2
c-74.4,13.3-147.2,26.3-222.2,39.7c10.5-20.4,20-39.1,30-58.6c-33.9-16.3-69-26.5-105.8-32.6c0-41.9,0-83.3,0-124.7
C793.4,403.3,846.4,418.4,923.1,448.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="198.4" height="78.66" xml:space="preserve">
<path d="M 0,0 0,13.88 10.97,13.88 10.97,10.31 60.69,10.31 60.69,0 0,0 z M 65.84,0 99.22,58.09 132.6,0 120.7,0 C 120.7,0 100.5,34.91 99.22,37.16 97.92,34.91 77.75,0 77.75,0 L 65.84,0 z M 137.8,0 137.8,13.88 148.7,13.88 148.7,10.31 198.4,10.31 198.4,0 137.8,0 z M 0,19.12 0,29.47 60.69,29.47 60.69,19.12 0,19.12 z M 137.8,19.12 137.8,29.47 198.4,29.47 198.4,19.12 137.8,19.12 z M 0,34.66 0,48.59 60.69,48.59 60.69,38.25 10.97,38.25 10.97,34.66 0,34.66 z M 137.8,34.66 137.8,48.59 198.4,48.59 198.4,38.25 148.7,38.25 148.7,34.66 137.8,34.66 z M 42.19,69.72 C 41.32,69.72 40.71,69.89 40.41,70.19 40.1,70.49 39.97,71.03 39.97,71.84 L 39.97,76.56 C 39.97,77.38 40.1,77.93 40.41,78.22 40.71,78.52 41.32,78.66 42.19,78.66 L 48.72,78.66 C 49.59,78.66 50.19,78.52 50.5,78.22 50.8,77.93 50.97,77.38 50.97,76.56 L 50.97,71.84 C 50.97,71.03 50.8,70.49 50.5,70.19 50.19,69.89 49.59,69.72 48.72,69.72 L 42.19,69.72 z M 64.37,69.72 64.37,78.66 66.25,78.66 66.25,73.84 C 66.25,73.66 66.23,73.43 66.22,73.19 66.2,72.94 66.18,72.69 66.16,72.41 66.26,72.53 66.38,72.67 66.5,72.78 66.62,72.89 66.75,73.01 66.91,73.16 L 73.47,78.66 74.88,78.66 74.88,69.72 73.03,69.72 73.03,74.41 C 73.03,74.52 73.05,74.7 73.06,74.91 73.07,75.11 73.09,75.47 73.12,75.97 72.99,75.81 72.82,75.66 72.66,75.5 72.49,75.35 72.31,75.18 72.09,75 L 65.81,69.72 64.37,69.72 z M 88.53,69.72 88.53,78.66 97.31,78.66 97.31,77 90.59,77 90.59,69.72 88.53,69.72 z M 109.4,69.72 109.4,78.66 111.5,78.66 111.5,69.72 109.4,69.72 z M 125.1,69.72 125.1,78.66 127,78.66 127,73.84 C 127,73.66 127,73.43 126.9,73.19 126.9,72.94 126.9,72.69 126.9,72.41 127,72.53 127.1,72.67 127.2,72.78 127.3,72.89 127.5,73.01 127.6,73.16 L 134.2,78.66 135.6,78.66 135.6,69.72 133.8,69.72 133.8,74.41 C 133.8,74.52 133.8,74.7 133.8,74.91 133.8,75.11 133.8,75.47 133.8,75.97 133.7,75.81 133.6,75.66 133.4,75.5 133.2,75.35 133,75.18 132.8,75 L 126.5,69.72 125.1,69.72 z M 149.3,69.72 149.3,78.66 158.5,78.66 158.5,77 151.3,77 151.3,74.78 155.4,74.78 155.4,73.25 151.3,73.25 151.3,71.25 158.4,71.25 158.4,69.72 149.3,69.72 z M 42.03,71.31 48.87,71.31 48.87,77 42.03,77 42.03,71.31 z" /></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

1
docs/static/img/providers/faceit.svg vendored Normal file
View File

@@ -0,0 +1 @@
<svg height="1520" viewBox="29.3 101.1 451.7 357.9" width="2500" xmlns="http://www.w3.org/2000/svg"><path d="m481 104.8c0-1.8-1.9-3.7-1.9-3.7-1.8 0-1.8 0-3.7 1.9-37.5 58.1-76.8 116.2-114.3 176.2h-326.2c-3.7 0-5.6 5.6-1.8 7.5 134.9 50.5 331.7 127.3 440.4 170.4 3.7 1.9 7.5-1.9 7.5-3.7z" fill="#fd5a00"/><path d="m481 104.8c0-1.8-1.9-3.7-1.9-3.7-1.8 0-1.8 0-3.7 1.9-37.5 58.1-76.8 116.2-114.3 176.2l119.9 1.23z" fill="#ff690a"/></svg>

After

Width:  |  Height:  |  Size: 432 B

100
docs/static/img/providers/fusionauth.svg vendored Normal file
View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 64 64.000001"
xml:space="preserve"
sodipodi:docname="fusionauthio-icon.svg"
width="64"
height="64"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
id="metadata49"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs47" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1001"
id="namedview45"
showgrid="false"
inkscape:zoom="4.205303"
inkscape:cx="94.166817"
inkscape:cy="80.099998"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:#EC8D53;}
.st1{fill:#FFFFFF;}
</style>
<g
id="g906"
transform="matrix(2.6397668,0,0,2.6397668,-35.322894,-50.457841)"><g
transform="matrix(0.15722805,0,0,0.15722805,13.258327,18.768605)"
style="fill:#ec8d53;fill-opacity:1"
id="g6">
<path
inkscape:connector-curvature="0"
style="fill:#ec8d53;fill-opacity:1"
id="path4"
d="m 77.7,2.2 c -3.9,0 -7.5,2.1 -9.4,5.5 -2.9,5.1 -1,11.7 4.2,14.5 1.6,0.9 3.4,1.3 5.2,1.3 3.9,0 7.5,-2.1 9.4,-5.5 C 90,12.8 88.1,6.3 82.9,3.5 81.2,2.7 79.4,2.2 77.7,2.2 Z"
class="st0" />
</g><g
transform="matrix(0.15722805,0,0,0.15722805,13.258327,18.768605)"
style="fill:#ec8d53;fill-opacity:1"
id="g10">
<path
inkscape:connector-curvature="0"
style="fill:#ec8d53;fill-opacity:1"
id="path8"
d="m 16,103.6 c -3.9,0 -7.5,2.1 -9.4,5.5 -2.9,5.1 -1,11.7 4.2,14.5 1.6,0.9 3.4,1.3 5.2,1.3 3.9,0 7.5,-2.1 9.4,-5.5 2.9,-5.2 1,-11.7 -4.2,-14.5 -1.6,-0.8 -3.4,-1.3 -5.2,-1.3 z"
class="st0" />
</g><g
transform="matrix(0.15722805,0,0,0.15722805,13.258327,18.768605)"
style="fill:#ec8d53;fill-opacity:1"
id="g14">
<path
inkscape:connector-curvature="0"
style="fill:#ec8d53;fill-opacity:1"
id="path12"
d="m 136.4,109.1 c -3.9,0 -7.5,2.1 -9.4,5.5 -2.9,5.1 -1,11.7 4.2,14.5 1.6,0.9 3.4,1.3 5.2,1.3 3.9,0 7.5,-2.1 9.4,-5.5 2.9,-5.2 1,-11.7 -4.2,-14.5 -1.6,-0.9 -3.4,-1.3 -5.2,-1.3 z"
class="st0" />
</g><path
inkscape:connector-curvature="0"
style="fill:#ec8d53;fill-opacity:1;stroke-width:0.15722805"
id="path40"
d="m 29.657213,20.891185 c -0.330179,-0.125783 -0.707527,0.04717 -0.849032,0.377347 -0.125782,0.330179 0.04717,0.707526 0.377347,0.849031 2.971611,1.132042 5.125635,3.490463 6.084726,6.304845 l -4.056484,1.084874 c -0.251565,-0.676081 -0.628912,-1.28927 -1.116319,-1.855291 -1.084873,-1.257825 -2.594263,-1.996797 -4.26088,-2.122579 -1.650895,-0.11006 -3.191729,0.440239 -4.37094,1.415052 l -2.877273,-2.845827 c 1.352161,-1.226379 3.034501,-2.091133 4.842624,-2.468481 -0.172951,-0.408793 -0.220119,-0.864754 -0.125783,-1.304992 -4.229434,0.833308 -7.814234,4.009315 -9.009167,8.301641 -0.39307,1.446498 -0.518852,2.924441 -0.361624,4.402385 0.03144,0.330179 0.314456,0.581744 0.644635,0.581744 0.03144,0 0.04717,0 0.07861,0 0.361624,-0.04717 0.613189,-0.361625 0.581743,-0.723249 -0.141505,-1.304993 -0.04717,-2.625709 0.314457,-3.899256 0.408792,-1.493666 1.147764,-2.830105 2.122578,-3.946424 l 2.892996,2.830105 c -0.754694,0.959091 -1.242101,2.138301 -1.336438,3.427571 -0.188674,2.720046 1.39933,5.141358 3.789196,6.147617 l -1.194933,3.930701 c -1.745232,-0.660357 -3.238898,-1.760954 -4.386663,-3.207452 -0.298733,0.345902 -0.67608,0.581744 -1.116319,0.691804 1.509389,1.949627 3.616245,3.396126 6.069003,4.072206 1.037705,0.283011 2.091133,0.424516 3.144561,0.424516 2.499926,0 4.93696,-0.817586 6.996648,-2.374144 0.28301,-0.220119 0.345902,-0.628912 0.125782,-0.911922 -0.220119,-0.283011 -0.628912,-0.345902 -0.911922,-0.125783 -2.499926,1.886737 -5.644487,2.547095 -8.663266,1.808123 l 1.194933,-3.946424 c 0.235842,0.04717 0.487407,0.07861 0.738972,0.09434 0.141505,0.01572 0.283011,0.01572 0.440239,0.01572 3.238898,0 5.958943,-2.531371 6.179062,-5.785992 0.03145,-0.471684 0.01572,-0.943368 -0.06289,-1.383607 l 4.056484,-1.084873 c 0.314456,1.556557 0.267287,3.191729 -0.172951,4.826901 -0.09434,0.314456 -0.188674,0.628912 -0.314456,0.943368 0.220119,0.04717 0.440238,0.125783 0.644635,0.235842 0.188673,0.11006 0.361624,0.235842 0.518852,0.39307 0.157228,-0.39307 0.298733,-0.801863 0.408793,-1.210656 1.588003,-5.817437 -1.430775,-11.807826 -7.059539,-13.96185 z m -9.213564,10.48711 c 0.172951,-2.609985 2.374143,-4.65395 4.984129,-4.65395 0.11006,0 0.235842,0 0.345902,0.01572 1.336438,0.09434 2.547094,0.691803 3.427571,1.698063 0.880477,1.006259 1.304993,2.295529 1.210656,3.631968 -0.09434,1.336438 -0.707526,2.547094 -1.713785,3.427571 -1.00626,0.880477 -2.29553,1.304993 -3.616246,1.210656 -2.751491,-0.188673 -4.826901,-2.57854 -4.638227,-5.330031 z"
class="st1" /><path
inkscape:connector-curvature="0"
style="fill:#ec8d53;fill-opacity:1;stroke-width:0.15722805"
id="path42"
d="M 29.216974,28.422408 C 28.336497,27.400426 27.110118,26.787236 25.77368,26.6929 c -0.11006,-0.01572 -0.235842,-0.01572 -0.345902,-0.01572 -2.625708,0 -4.826901,2.059687 -5.015575,4.685396 -0.188673,2.767213 1.90246,5.172803 4.669673,5.361476 2.782937,0.204397 5.172803,-1.933905 5.361477,-4.669673 0.09434,-1.336438 -0.345902,-2.625708 -1.226379,-3.631968 z m -3.773473,6.383459 c -0.754695,0 -1.367884,0.06289 -1.367884,-0.345902 l 0.597466,-3.411848 c -0.345901,-0.235843 -0.566021,-0.628913 -0.566021,-1.084874 0,-0.723249 0.597467,-1.320716 1.320716,-1.320716 0.723249,0 1.320716,0.597467 1.320716,1.320716 0,0.440239 -0.22012,0.833309 -0.566021,1.084874 l 0.613189,3.380403 c 0,0.01572 0,0.01572 0,0.03144 0.01572,0.408793 -0.597466,0.345902 -1.352161,0.345902 z"
class="st0" /></g>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

1
docs/static/img/providers/kakao.svg vendored Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" viewBox="0 0 256 256"><path fill="#FFE812" d="M256 236c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20V20C0 8.954 8.954 0 20 0h216c11.046 0 20 8.954 20 20v216z"/><path d="M128 36C70.562 36 24 72.713 24 118c0 29.279 19.466 54.97 48.748 69.477-1.593 5.494-10.237 35.344-10.581 37.689 0 0-.207 1.762.934 2.434s2.483.15 2.483.15c3.272-.457 37.943-24.811 43.944-29.04 5.995.849 12.168 1.29 18.472 1.29 57.438 0 104-36.712 104-82 0-45.287-46.562-82-104-82z"/><path fill="#FFE812" d="M70.5 146.625c-3.309 0-6-2.57-6-5.73V105.25h-9.362c-3.247 0-5.888-2.636-5.888-5.875s2.642-5.875 5.888-5.875h30.724c3.247 0 5.888 2.636 5.888 5.875s-2.642 5.875-5.888 5.875H76.5v35.645c0 3.16-2.691 5.73-6 5.73zM123.112 146.547c-2.502 0-4.416-1.016-4.993-2.65l-2.971-7.778-18.296-.001-2.973 7.783c-.575 1.631-2.488 2.646-4.99 2.646a9.155 9.155 0 0 1-3.814-.828c-1.654-.763-3.244-2.861-1.422-8.52l14.352-37.776c1.011-2.873 4.082-5.833 7.99-5.922 3.919.088 6.99 3.049 8.003 5.928l14.346 37.759c1.826 5.672.236 7.771-1.418 8.532a9.176 9.176 0 0 1-3.814.827c-.001 0 0 0 0 0zm-11.119-21.056L106 108.466l-5.993 17.025h11.986zM138 145.75c-3.171 0-5.75-2.468-5.75-5.5V99.5c0-3.309 2.748-6 6.125-6s6.125 2.691 6.125 6v35.25h12.75c3.171 0 5.75 2.468 5.75 5.5s-2.579 5.5-5.75 5.5H138zM171.334 146.547c-3.309 0-6-2.691-6-6V99.5c0-3.309 2.691-6 6-6s6 2.691 6 6v12.896l16.74-16.74c.861-.861 2.044-1.335 3.328-1.335 1.498 0 3.002.646 4.129 1.772 1.051 1.05 1.678 2.401 1.764 3.804.087 1.415-.384 2.712-1.324 3.653l-13.673 13.671 14.769 19.566a5.951 5.951 0 0 1 1.152 4.445 5.956 5.956 0 0 1-2.328 3.957 5.94 5.94 0 0 1-3.609 1.211 5.953 5.953 0 0 1-4.793-2.385l-14.071-18.644-2.082 2.082v13.091a6.01 6.01 0 0 1-6.002 6.003z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

1
docs/static/img/providers/mailru.svg vendored Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 115 44" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><rect id="h44px_-mail_-_bg" serif:id="h44px_@mail_&amp;_bg" x="0" y="0" width="115" height="44" style="fill:none;"/><clipPath id="_clip1"><rect x="0" y="0" width="115" height="44"/></clipPath><g clip-path="url(#_clip1)"><path d="M115,4c0,-2.208 -1.792,-4 -4,-4l-107,0c-2.208,0 -4,1.792 -4,4l0,36c0,2.208 1.792,4 4,4l107,0c2.208,0 4,-1.792 4,-4l0,-36Z" style="fill:#005ff9;"/><g><rect x="16" y="8" width="83" height="28" style="fill:none;"/><rect x="95.68" y="10.59" width="2.654" height="18.019" style="fill:#fff;"/><path d="M92.812,28.609l-2.654,0l0,-12.769l2.654,0l0,12.769Zm-1.327,-18.467c0.989,0 1.792,0.803 1.792,1.792c0,0.989 -0.803,1.792 -1.792,1.792c-0.989,0 -1.792,-0.803 -1.792,-1.792c0,-0.989 0.803,-1.792 1.792,-1.792Z" style="fill:#fff;"/><path d="M87.29,28.609l-2.608,0l0,-1.397c-0.935,1.059 -2.582,1.725 -4.131,1.725c-3.704,0 -6.712,-3.008 -6.712,-6.713c0,-3.704 3.008,-6.712 6.712,-6.712c1.549,0 3.109,0.584 4.131,1.615l0,-1.287l2.608,0l0,12.769Zm-6.654,-10.414c2.284,0 4.081,1.634 4.081,4.029c0,2.395 -1.797,4.044 -4.081,4.044c-2.284,0 -4.02,-1.759 -4.02,-4.044c0,-2.284 1.736,-4.029 4.02,-4.029Z" style="fill:#fff;"/><path d="M54.361,28.609l-2.609,0l0,-12.769l2.609,0l0,0.892c0.579,-0.545 1.665,-1.218 3.171,-1.22c1.852,0 3.232,0.779 4.177,2.032c1.034,-1.241 2.739,-2.032 4.476,-2.032c3.26,0 5.482,2.201 5.482,5.631l0,7.466l-2.609,0l0,-7.466c0,-1.666 -1.356,-3.022 -3.022,-3.022c-1.667,0 -3.022,1.356 -3.022,3.022l0,7.466l-2.609,0l0,-7.466c0,-1.666 -1.356,-3.022 -3.022,-3.022c-1.666,0 -3.022,1.356 -3.022,3.022l0,7.466Z" style="fill:#fff;"/><path d="M34.211,22c0,2.322 -1.889,4.211 -4.211,4.211c-2.322,0 -4.211,-1.889 -4.211,-4.211c0,-2.322 1.889,-4.211 4.211,-4.211c2.322,0 4.211,1.889 4.211,4.211m-4.211,-14c-7.72,0 -14,6.28 -14,14c0,7.72 6.28,14 14,14c2.828,0 5.555,-0.842 7.886,-2.435l0.04,-0.028l-1.886,-2.192l-0.032,0.02c-1.794,1.155 -3.872,1.765 -6.008,1.765c-6.137,0 -11.13,-4.993 -11.13,-11.13c0,-6.137 4.993,-11.13 11.13,-11.13c6.137,0 11.13,4.993 11.13,11.13c0,0.795 -0.089,1.6 -0.262,2.392c-0.352,1.445 -1.364,1.887 -2.123,1.829c-0.764,-0.062 -1.658,-0.606 -1.664,-1.938l0,-1.015l0,-1.268c0,-3.905 -3.176,-7.081 -7.081,-7.081c-3.905,0 -7.081,3.176 -7.081,7.081c0,3.905 3.176,7.081 7.081,7.081c1.897,0 3.676,-0.741 5.017,-2.09c0.78,1.214 2.051,1.975 3.498,2.091c0.124,0.01 0.251,0.015 0.376,0.015c1.019,0 2.028,-0.341 2.842,-0.958c0.839,-0.638 1.466,-1.559 1.812,-2.666c0.055,-0.179 0.157,-0.588 0.157,-0.591l0.003,-0.015c0.204,-0.888 0.295,-1.773 0.295,-2.867c0,-7.72 -6.28,-14 -14,-14" style="fill:#ff9e00;fill-rule:nonzero;"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

8
docs/static/img/providers/meidum.svg vendored Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 -55 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M72.2009141,1.42108547e-14 C112.076502,1.42108547e-14 144.399375,32.5485469 144.399375,72.6964154 C144.399375,112.844284 112.074049,145.390378 72.2009141,145.390378 C32.327779,145.390378 0,112.844284 0,72.6964154 C0,32.5485469 32.325326,1.42108547e-14 72.2009141,1.42108547e-14 Z M187.500628,4.25836743 C207.438422,4.25836743 223.601085,34.8960455 223.601085,72.6964154 L223.603538,72.6964154 C223.603538,110.486973 207.440875,141.134463 187.503081,141.134463 C167.565287,141.134463 151.402624,110.486973 151.402624,72.6964154 C151.402624,34.9058574 167.562834,4.25836743 187.500628,4.25836743 Z M243.303393,11.3867175 C250.314,11.3867175 256,38.835526 256,72.6964154 C256,106.547493 250.316453,134.006113 243.303393,134.006113 C236.290333,134.006113 230.609239,106.554852 230.609239,72.6964154 C230.609239,38.837979 236.292786,11.3867175 243.303393,11.3867175 Z" fill="#000000">
</path>
</g>

After

Width:  |  Height:  |  Size: 1.2 KiB

26
docs/static/img/providers/naver.svg vendored Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="521.0783691px" height="100px" viewBox="0 0 521.0783691 100" style="enable-background:new 0 0 521.0783691 100;"
xml:space="preserve">
<style type="text/css">
.st0{fill:#03CF5D;}
</style>
<g>
<path class="st0" d="M150,0l-39.2157059,100h33.3333054l4.7360992-13.2352982h37.5867004L191.1764984,100h33.3332977L185.2940979,0
H150z M156.4685974,62.7450981L167.64711,31.5069008l11.1783905,31.2381973H156.4685974z"/>
<polygon class="st0" points="363.2352905,62.0098 408.8234863,62.0098 408.8234863,37.9902 363.2352905,37.9902
363.2352905,24.5098 409.3136902,24.5098 409.3136902,0 331.3724976,0 331.3724976,100 410.2940979,100 410.2940979,75.4901962
363.2352905,75.4901962 "/>
<polygon class="st0" points="264.2156982,65.7534027 240.6862946,0 207.35289,0 246.5685883,100 281.8627014,100 321.0783997,0
287.7451172,0 "/>
<polygon class="st0" points="68.1371994,53.5211983 30.8822994,0 0,0 0,100 32.3528976,100 32.3528976,46.4789009 69.6077957,100
100.4901962,100 100.4901962,0 68.1371994,0 "/>
<path class="st0" d="M495.7964783,65.694397l3.0326233-1.2691956
c11.6220093-4.8646011,17.5325928-15.3901024,17.5325928-28.7482033c0-12.6320972-4.6071167-21.9770985-13.6939087-27.7756977
C494.2207947,2.5106001,483.2662964,0,468.1940002,0h-42.213623v100h31.3724976V72.0588989h11.2745056L487.7451172,100h33.333252
L495.7964783,65.694397z M474.0195923,46.5686989h-17.1568909V25.4902h17.1568909
c5.8205872,0,10.5391846,4.7185993,10.5391846,10.5391998C484.5587769,41.8501015,479.8401794,46.5686989,474.0195923,46.5686989z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

1
docs/static/img/providers/netlify.svg vendored Normal file
View File

@@ -0,0 +1 @@
<svg viewBox="0 0 256 105" xmlns="http://www.w3.org/2000/svg" data-theme="light" class="netlify-logo-full-small netlify-logo masthead-home-logo"><g fill="var(--_netlify-logo-lines-color)" class="netlify-logo-full-small-lines"><path d="M58.4705 103.765V77.4144L59.0166 76.8683H65.6043L66.1505 77.4144V103.765L65.6043 104.311H59.0166L58.4705 103.765Z"></path> <path d="M58.4705 26.8971V0.546133L59.0166 0H65.6043L66.1505 0.546133V26.8971L65.6043 27.4432H59.0166L58.4705 26.8971Z"></path> <path d="M35.7973 85.2395H34.8928L30.3616 80.7083V79.8037L38.8522 71.3045L43.648 71.3131L44.288 71.9445V76.7403L35.7973 85.2395Z"></path> <path d="M30.3616 24.7467V23.8336L34.8928 19.3109H35.7973L44.288 27.8016V32.5888L43.648 33.2373H38.8522L30.3616 24.7467Z"></path> <path d="M0.546133 48.3072H37.8795L38.4256 48.8533V55.4496L37.8795 55.9957H0.546133L0 55.4496V48.8533L0.546133 48.3072Z"></path> <path d="M220.314 48.3157H255.445L255.991 48.8619V55.4496L255.445 55.9957H217.566L217.02 55.4496L219.759 48.8619L220.305 48.3157H220.314Z"></path></g> <g fill="var(--_netlify-logo-text-color)" class="netlify-logo-full-small-text"><path d="M74.6666 65.8859H68.0789L67.5328 65.3397V49.92C67.5328 47.1723 66.4576 45.0475 63.1466 44.9792C61.44 44.9365 59.4944 44.9792 57.4122 45.0645L57.0965 45.3803V51.6096V65.3312L56.5504 65.8773H49.9626L49.4165 65.3312V38.9803L49.9626 38.4341H64.785C70.545 38.4341 75.2128 43.1019 75.2128 48.8619V65.3312L74.6666 65.8773V65.8859Z"></path> <path d="M106.573 54.3488L106.027 54.8949H88.9942L88.448 55.4411C88.448 56.5419 89.5488 59.8357 93.9435 59.8357C95.5904 59.8357 97.2374 59.2896 97.792 58.1888L98.3382 57.6427H104.926L105.472 58.1888C104.926 61.4827 102.178 66.432 93.935 66.432C84.5995 66.432 80.2048 59.8443 80.2048 52.1472C80.2048 44.4501 84.5995 37.8624 93.3888 37.8624C102.178 37.8624 106.573 44.4501 106.573 52.1472V54.3403V54.3488ZM98.3296 48.8533C98.3296 48.3072 97.7835 44.4587 93.3888 44.4587C88.9942 44.4587 88.448 48.3072 88.448 48.8533L88.9942 49.3995H97.7835L98.3296 48.8533Z"></path> <path d="M121.95 57.6427C121.95 58.7435 122.496 59.2896 123.597 59.2896H128.538L129.084 59.8358V65.3312L128.538 65.8774H123.597C118.656 65.8774 114.261 63.6758 114.261 57.6342V45.5509L113.715 45.0048H109.867L109.321 44.4587V38.9632L109.867 38.4171H113.715L114.261 37.8709V32.9301L114.807 32.384H121.395L121.941 32.9301V37.8709L122.487 38.4171H128.529L129.075 38.9632V44.4587L128.529 45.0048H122.487L121.941 45.5509V57.6342L121.95 57.6427Z"></path> <path d="M142.276 65.8859H135.688L135.142 65.3397V27.9808L135.688 27.4347H142.276L142.822 27.9808V65.3312L142.276 65.8773V65.8859Z"></path> <path d="M157.107 34.0224H150.519L149.973 33.4763V27.9808L150.519 27.4347H157.107L157.653 27.9808V33.4763L157.107 34.0224ZM157.107 65.8859H150.519L149.973 65.3397V38.9717L150.519 38.4256H157.107L157.653 38.9717V65.3397L157.107 65.8859Z"></path> <path d="M182.929 27.9808V33.4763L182.383 34.0224H177.442C176.341 34.0224 175.795 34.5685 175.795 35.6693V37.8709L176.341 38.4171H181.837L182.383 38.9632V44.4587L181.837 45.0048H176.341L175.795 45.5509V65.3227L175.249 65.8688H168.661L168.115 65.3227V45.5509L167.569 45.0048H163.72L163.174 44.4587V38.9632L163.72 38.4171H167.569L168.115 37.8709V35.6693C168.115 29.6277 172.51 27.4261 177.451 27.4261H182.391L182.938 27.9723L182.929 27.9808Z"></path> <path d="M203.247 66.432C201.045 71.9275 198.852 75.2213 191.164 75.2213H188.416L187.87 74.6752V69.1797L188.416 68.6336H191.164C193.911 68.6336 194.458 68.0875 195.012 66.4405V65.8944L186.223 44.4672V38.9717L186.769 38.4256H191.71L192.256 38.9717L198.844 57.6512H199.39L205.978 38.9717L206.524 38.4256H211.465L212.011 38.9717V44.4672L203.221 66.4405L203.247 66.432Z"></path></g></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

5
docs/static/img/providers/osso.svg vendored Normal file
View File

@@ -0,0 +1,5 @@
<svg width="104" height="104" viewBox="0 0 104 104" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26.8125 32.4899C26.8125 32.4899 78.8125 12.1875 78.8125 29.2398C78.8125 46.2922 27.6249 56.875 27.6249 75.4834C27.6249 94.0919 78.8125 72.2335 78.8125 72.2335" stroke="white" stroke-width="4.875" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.8125 11.375C38.0308 11.375 47.125 20.4692 47.125 31.6875C47.125 42.9058 38.0308 52 26.8125 52C15.5942 52 6.5 42.9058 6.5 31.6875C6.5 20.4692 15.5942 11.375 26.8125 11.375ZM27.2188 21.5312C32.6035 21.5312 36.9688 25.8965 36.9688 31.2812V32.0938C36.9688 37.4785 32.6035 41.8438 27.2188 41.8438H26.4062C21.0215 41.8438 16.6562 37.4785 16.6562 32.0938V31.2812C16.6562 25.8965 21.0215 21.5312 26.4062 21.5312H27.2188Z" fill="#FFCD83"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M78.8125 92.625C67.5942 92.625 58.5 83.5308 58.5 72.3125C58.5 61.0942 67.5942 52 78.8125 52C90.0308 52 99.125 61.0942 99.125 72.3125C99.125 83.5308 90.0308 92.625 78.8125 92.625ZM78.4062 82.4688C73.0215 82.4688 68.6562 78.1035 68.6562 72.7188V71.9062C68.6562 66.5215 73.0215 62.1562 78.4062 62.1562H79.2188C84.6035 62.1562 88.9688 66.5215 88.9688 71.9062V72.7188C88.9688 78.1035 84.6035 82.4688 79.2188 82.4688H78.4062Z" fill="#FFCD83"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

1
docs/static/img/providers/osu.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="64" height="64"><path d="M16.132 0a16 16 0 0 0-5.771 30.952c-.13-1.312-.262-3.148 0-4.6l1.836-8a5.771 5.771 0 0 1-.525-2.361c0-2.23 1.312-3.935 2.885-3.935s1.967 1.05 1.967 2.23-.918 3.4-1.312 5.377.787 2.885 2.36 2.885 4.984-3.016 4.984-7.344-2.754-6.558-6.69-6.558-7.082 3.54-7.082 7.082c0 1.312.525 2.885 1.18 3.672a.525.525 0 0 1 .131.393l-.393 1.836c-.13.262-.262.393-.525.262-1.967-.918-3.28-3.803-3.28-6.164 0-4.984 3.672-9.705 10.623-9.705s9.836 3.935 9.836 9.18-3.54 9.968-8.263 9.968c-1.574 0-3.148-.787-3.672-1.836l-1.05 3.803c-.393 1.443-1.312 3.148-1.967 4.197A16 16 0 1 0 16.132 0z" fill="#bd081c"/></svg>

After

Width:  |  Height:  |  Size: 686 B

28
docs/static/img/providers/pipedrive.svg vendored Normal file
View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="940" height="250" version="1.1" viewBox="0 0 940 250" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style type="text/css">
.st0{clip-path:url(#SVGID_2_);fill:#203232;}
</style>
<g transform="matrix(1.4291 0 0 1.4291 -77.63 -55.768)">
<defs>
<rect id="SVGID_1_" width="755.8" height="253.2"/>
</defs>
<clipPath id="SVGID_2_">
<use width="100%" height="100%" xlink:href="#SVGID_1_"/>
</clipPath><g fill="#203232">
<path class="st0" d="m128.3 87.8c-11.9 0-18.8 5.4-22.1 9-0.4-3.2-2.5-7.3-10.7-7.3h-17.9v18.6h7.3c1.2 0 1.6 0.4 1.6 1.6v85.1h21.2v-31.8-2.4c3.3 3 9.6 7.2 19.5 7.2 20.7 0 35.2-16.4 35.2-40 0.1-23.9-13.7-40-34.1-40m-4.3 61.5c-11.4 0-16.6-10.9-16.6-21.1 0-16 8.7-21.7 16.9-21.7 10 0 16.8 8.6 16.8 21.5-0.1 14.8-8.7 21.3-17.1 21.3" clip-path="url(#SVGID_2_)"/>
<path class="st0" d="m191 146v-45.1c0-7.6-3.7-11.3-11.2-11.3h-19v18.6h7.3c1.2 0 1.6 0.4 1.6 1.6v44.9c0 7.7 3.6 11.3 11.2 11.3h19v-18.5h-7.3c-1.1 0.1-1.6-0.4-1.6-1.5" clip-path="url(#SVGID_2_)"/>
<path class="st0" d="m246.4 87.8c-11.9 0-18.8 5.4-22.2 9-0.4-3.2-2.5-7.3-10.7-7.3h-17.8v18.6h7.3c1.2 0 1.6 0.4 1.6 1.6v85.1h21.4v-31.8-2.4c3.3 3 9.7 7.2 19.5 7.2 20.7 0 35.2-16.4 35.2-40 0-23.9-13.8-40-34.3-40m-4.3 61.5c-11.4 0-16.6-10.9-16.6-21.1 0-16 8.7-21.7 16.9-21.7 10 0 16.8 8.6 16.8 21.5 0 14.8-8.6 21.3-17.1 21.3" clip-path="url(#SVGID_2_)"/>
<path class="st0" d="m320.6 87.8c-22.9 0-38.8 16.5-38.8 40 0 23.2 17.2 40 41 40 18.7 0 30.1-11.1 30.6-11.5l0.9-0.8-9.2-15.3-1.4 1.3c-0.1 0.1-8.5 7.8-19.6 7.8-10.6 0-18.4-6.5-20.3-16.7h50.3l0.1-1.4c0-0.2 0.5-4.7 0.5-6.9 0-21.8-13.7-36.5-34.1-36.5m-16 29c2.3-7.5 8.1-11.8 16-11.8 6.4 0 11.3 4.9 12.2 11.8z" clip-path="url(#SVGID_2_)"/>
<path class="st0" d="m431.9 146v-73.9c0-7.6-3.7-11.3-11.3-11.3h-19v18.6h7.3c1.2 0 1.6 0.4 1.6 1.6v13.4c-3.1-2.8-9.2-6.5-19.8-6.5-20.6 0-35 16.5-35 40 0 23.9 13.8 40 34.2 40 11.7 0 18.5-5.4 21.8-9.2 0.4 3.3 2.6 7.5 10.5 7.5h18.4v-18.5h-7.1c-1.2-0.1-1.6-0.6-1.6-1.7m-37.9 3.3c-10.1 0-16.6-8.4-16.6-21.5 0-12.7 6.8-21.2 16.9-21.2 11.5 0 16.6 10.6 16.6 21.1 0 16-8.7 21.6-16.9 21.6" clip-path="url(#SVGID_2_)"/>
<path class="st0" d="m487.9 88.5c-8.8 0-17 5.1-21.8 13.2v-1.7c0-6.9-3.8-10.5-11.3-10.5h-18.2v18.6h7.3c1.2 0 1.6 0.4 1.6 1.6v56.3h21.2v-30.6c0-4 0.5-7.9 1.5-11.5 3.2-10.6 11.7-14.3 18.5-14.3 2.4 0 4.2 0.3 4.2 0.3l1.8 0.3v-21.1l-1.3-0.2c0-0.1-1.8-0.4-3.5-0.4" clip-path="url(#SVGID_2_)"/>
<path class="st0" d="m522.9 146v-45.1c0-7.6-3.7-11.3-11.2-11.3h-17.2v18.6h5.5c1.2 0 1.6 0.4 1.6 1.6v44.9c0 7.7 3.6 11.3 11.2 11.3h19v-18.5h-7.3c-1.1 0.1-1.6-0.4-1.6-1.5" clip-path="url(#SVGID_2_)"/>
<path class="st0" d="m603 89.6h-11.3c-6.6 0-10.2 2.3-12.3 7.9l-13.8 38.6c-0.5 1.4-0.9 3-1.3 4.6-0.4-1.5-0.9-3.2-1.4-4.6l-13.8-38.6c-2.1-5.7-5.5-7.9-12.3-7.9h-12v18.6h3.1c1.7 0 2.2 0.6 2.6 1.6l21.4 56.3h23.9l21.4-56.3c0.4-1 0.9-1.6 2.6-1.6h3.3v-18.6z" clip-path="url(#SVGID_2_)"/>
<path class="st0" d="m639.6 87.8c-22.9 0-38.8 16.5-38.8 40 0 23.2 17.2 40 41 40 18.7 0 30.1-11.1 30.6-11.5l0.9-0.8-9.2-15.3-1.4 1.3c-0.1 0.1-8.5 7.8-19.5 7.8-10.6 0-18.4-6.5-20.3-16.7h50.3l0.1-1.4c0-0.2 0.4-4.7 0.4-6.9 0-21.8-13.7-36.5-34.1-36.5m-16 29c2.3-7.5 8.1-11.8 16-11.8 6.4 0 11.3 4.9 12.2 11.8z" clip-path="url(#SVGID_2_)"/>
<rect class="st0" x="170.6" y="60.7" width="18.9" height="19.9" clip-path="url(#SVGID_2_)"/>
<rect class="st0" x="502.6" y="60.7" width="18.9" height="19.9" clip-path="url(#SVGID_2_)"/>
<polygon class="st0" points="673.3 89.6 676.1 89.6 676.1 97.2 678.2 97.2 678.2 89.6 681 89.6 681 87.8 673.3 87.8" clip-path="url(#SVGID_2_)"/>
<polygon class="st0" points="689.3 87.8 687.2 94.3 685 87.8 682.1 87.8 682.1 97.2 684 97.2 684 90.6 684.1 90.6 686.4 97.2 688 97.2 690.3 90.6 690.3 97.2 692.2 97.2 692.2 87.8" clip-path="url(#SVGID_2_)"/>
</g></g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

25
docs/static/img/providers/saleforce.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.9 KiB

3
docs/static/img/providers/strava.svg vendored Normal file
View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 432 91"><style type="text/css">
.st0{fill:#FC4C02;}
</style><path class="st0" d="M74.5 49.5c1.6 2.8 2.5 6.3 2.5 10.4v0.2c0 4.2-0.8 8-2.5 11.4 -1.7 3.4-4.1 6.2-7.1 8.6 -3.1 2.3-6.8 4.1-11.2 5.4 -4.4 1.3-9.3 1.9-14.7 1.9 -8.2 0-15.9-1.1-23-3.4 -7.1-2.3-13.2-5.7-18.3-10.2l14.4-17.1c4.4 3.4 9 5.8 13.8 7.2 4.8 1.5 9.6 2.2 14.4 2.2 2.5 0 4.2-0.3 5.3-0.9 1.1-0.6 1.6-1.5 1.6-2.5v-0.2c0-1.2-0.8-2.1-2.4-2.9 -1.6-0.8-4.5-1.6-8.8-2.4 -4.5-0.9-8.8-2-12.9-3.2 -4.1-1.2-7.7-2.8-10.8-4.7 -3.1-1.9-5.6-4.3-7.4-7.2C5.4 39 4.5 35.4 4.5 31.2V31c0-3.8 0.7-7.4 2.2-10.7 1.5-3.3 3.7-6.2 6.6-8.6 2.9-2.5 6.5-4.4 10.7-5.8 4.2-1.4 9.1-2.1 14.7-2.1 7.8 0 14.7 0.9 20.5 2.8 5.9 1.8 11.1 4.6 15.8 8.3L61.9 33c-3.8-2.8-7.9-4.8-12.1-6.1 -4.3-1.3-8.3-1.9-12-1.9 -2 0-3.5 0.3-4.4 0.9 -1 0.6-1.4 1.4-1.4 2.4v0.2c0 1.1 0.7 2 2.2 2.8 1.5 0.8 4.3 1.6 8.5 2.4 5.1 0.9 9.8 2 14 3.3 4.2 1.3 7.8 3 10.9 5C70.5 44.2 72.9 46.6 74.5 49.5zM75.5 28.1h23.7v57.8h26.9V28.1h23.7V5.3H75.5V28.1zM387.9 0.3l-43.3 85.6h25.8l17.5-34.6 17.6 34.6h25.8L387.9 0.3zM267.3 0.3l43.4 85.6h-25.8l-17.5-34.6 -17.5 34.6h-17.5 -8.3 -22.4l-15.2-23h-0.2 -5.5v23h-26.9V5.3H193c7.2 0 13.1 0.8 17.8 2.5 4.6 1.6 8.4 3.9 11.2 6.7 2.5 2.4 4.3 5.2 5.5 8.3 1.2 3.1 1.8 6.7 1.8 10.8v0.2c0 5.9-1.4 10.9-4.3 14.9 -2.8 4.1-6.7 7.3-11.6 9.7l14 20.4L267.3 0.3zM202.5 35.6c0-2.6-0.9-4.5-2.8-5.8 -1.8-1.3-4.3-1.9-7.5-1.9h-11.7v15.8h11.6c3.2 0 5.8-0.7 7.6-2.1 1.8-1.4 2.8-3.3 2.8-5.8V35.6zM345.2 5.3L327.6 40 310 5.3h-25.8l43.4 85.6 43.3-85.6H345.2z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 122.52 122.523" xmlns="http://www.w3.org/2000/svg"><g fill="#464342"><path d="m8.708 61.26c0 20.802 12.089 38.779 29.619 47.298l-25.069-68.686c-2.916 6.536-4.55 13.769-4.55 21.388z"/><path d="m96.74 58.608c0-6.495-2.333-10.993-4.334-14.494-2.664-4.329-5.161-7.995-5.161-12.324 0-4.831 3.664-9.328 8.825-9.328.233 0 .454.029.681.042-9.35-8.566-21.807-13.796-35.489-13.796-18.36 0-34.513 9.42-43.91 23.688 1.233.037 2.395.063 3.382.063 5.497 0 14.006-.667 14.006-.667 2.833-.167 3.167 3.994.337 4.329 0 0-2.847.335-6.015.501l19.138 56.925 11.501-34.493-8.188-22.434c-2.83-.166-5.511-.501-5.511-.501-2.832-.166-2.5-4.496.332-4.329 0 0 8.679.667 13.843.667 5.496 0 14.006-.667 14.006-.667 2.835-.167 3.168 3.994.337 4.329 0 0-2.853.335-6.015.501l18.992 56.494 5.242-17.517c2.272-7.269 4.001-12.49 4.001-16.989z"/><path d="m62.184 65.857-15.768 45.819c4.708 1.384 9.687 2.141 14.846 2.141 6.12 0 11.989-1.058 17.452-2.979-.141-.225-.269-.464-.374-.724z"/><path d="m107.376 36.046c.226 1.674.354 3.471.354 5.404 0 5.333-.996 11.328-3.996 18.824l-16.053 46.413c15.624-9.111 26.133-26.038 26.133-45.426.001-9.137-2.333-17.729-6.438-25.215z"/><path d="m61.262 0c-33.779 0-61.262 27.481-61.262 61.26 0 33.783 27.483 61.263 61.262 61.263 33.778 0 61.265-27.48 61.265-61.263-.001-33.779-27.487-61.26-61.265-61.26zm0 119.715c-32.23 0-58.453-26.223-58.453-58.455 0-32.23 26.222-58.451 58.453-58.451 32.229 0 58.45 26.221 58.45 58.451 0 32.232-26.221 58.455-58.45 58.455z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 295 81" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,0,-107)">
<g id="zitadel-logo-dark" transform="matrix(1,0,0,1,-20.9181,18.2562)">
<rect x="20.918" y="89.57" width="294.943" height="79.632" style="fill:none;"/>
<g transform="matrix(2.73916,0,0,1.55095,-35271.3,23.6234)">
<g transform="matrix(0.160219,-0.0758207,0.0429306,0.282967,12622.8,-105.843)">
<path d="M1493.5,1056.38L1493.5,1037L1496.5,1037L1496.5,1061.62L1426.02,1020.38L1496.5,979.392L1496.5,1004L1493.5,1004L1493.5,984.608L1431.98,1020.39L1493.5,1056.38Z" style="fill:rgb(16,16,16);"/>
</g>
<g>
<g transform="matrix(-0.0429306,-0.282967,0.160219,-0.0758207,12884.5,137.392)">
<path d="M212.517,110L200.392,110L190,92L179.608,110L167.483,110L190,71L212.517,110Z" style="fill:url(#_Linear1);"/>
</g>
<g transform="matrix(0.160219,0.0758207,-0.0429306,0.282967,12878.9,10.8747)">
<path d="M212.517,110L200.392,110L190,92L179.608,110L167.483,110L190,71L212.517,110Z" style="fill:url(#_Linear2);"/>
</g>
<g transform="matrix(-0.117289,0.207146,-0.117289,-0.207146,12943.8,65.7)">
<path d="M212.517,110L200.392,110L190,92L179.608,110L167.483,110L190,71L212.517,110Z" style="fill:url(#_Linear3);"/>
</g>
<g transform="matrix(-0.160219,-0.0758207,0.0429306,-0.282967,12917.4,132.195)">
<path d="M139.622,117L149,142L130.244,142L139.622,117Z" style="fill:url(#_Linear4);"/>
</g>
<g transform="matrix(-0.117289,0.207146,0.117289,0.207146,12897.8,5.87512)">
<path d="M139.622,117L149,142L130.244,142L139.622,117Z" style="fill:url(#_Linear5);"/>
</g>
<g transform="matrix(-0.0429306,-0.282967,-0.160219,0.0758207,12936.8,97.6441)">
<path d="M139.622,117L149,142L130.244,142L139.622,117Z" style="fill:url(#_Linear6);"/>
</g>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0429306,0.282967,12622.6,-105.767)">
<circle cx="1496" cy="1004" r="7" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0429306,0.282967,12624.1,-96.4295)">
<circle cx="1496" cy="1004" r="7" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0429306,0.282967,12625,-90.2042)">
<circle cx="1496" cy="1004" r="7" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0429306,0.282967,12612.8,-96.1272)">
<circle cx="1496" cy="1004" r="7" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0429306,0.282967,12621.7,-111.993)">
<circle cx="1496" cy="1004" r="7" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0375643,0.247596,12637.2,-10.9628)">
<path d="M1499.26,757.787C1499.26,757.787 1497.37,756.489 1497,755.2C1496.71,754.182 1496.57,750.662 1496.54,750C1496.41,747.303 1499.21,745.644 1499.21,745.644L1490.01,745.835C1490.01,745.835 1493.15,745.713 1493.46,750C1493.51,750.661 1493.23,753.476 1493,755.2C1492.91,756.447 1491.2,757.668 1491.2,757.668L1499.26,757.787Z" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0375643,0.247596,12639.5,4.60032)">
<path d="M1495,760L1495,744" style="fill:none;"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0375643,0.247596,12639.5,4.60032)">
<path d="M1498.27,757.077C1498.27,757.077 1496.71,756.46 1496.65,754.8C1496.65,753.658 1496.64,753.281 1496.65,752.016C1496.62,751.334 1496.59,750.608 1496.65,749.949C1496.78,746.836 1498.5,746.156 1498.5,746.156L1491.46,745.931C1491.46,745.931 1493.37,746.719 1493.65,749.83C1493.71,750.489 1493.69,751.528 1493.65,752.209C1493.64,753.331 1493.64,753.413 1493.65,754.518C1493.68,756.334 1492.58,756.827 1492.58,756.827L1498.27,757.077Z" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0375643,0.247596,12627.7,-0.733956)">
<path d="M1496.17,759.473L1555.54,720.014" style="fill:none;"/>
</g>
<g transform="matrix(0.160219,-0.0758207,0.0375643,0.247596,12627.7,-0.733956)">
<path d="M1500.86,762.056C1500.86,762.056 1499.86,760.4 1503.09,757.456C1504.91,755.797 1507.33,754.151 1509.98,752.255C1514.82,748.79 1520.68,744.94 1526.52,741.049C1531.45,737.766 1536.38,734.479 1540.82,731.68C1544.52,729.349 1547.85,727.296 1550.54,725.8C1551.07,725.506 1551.6,725.329 1552.05,725.029C1554.73,723.257 1556.85,724.968 1556.85,724.968L1552.23,716.282C1552.23,716.282 1551.99,719.454 1550,720.997C1549.57,721.333 1549.15,721.741 1548.67,722.12C1546.2,724.053 1542.99,726.344 1539.39,728.867C1535.06,731.898 1530.13,735.166 1525.19,738.438C1519.35,742.314 1513.52,746.234 1508.49,749.329C1505.74,751.023 1503.28,752.577 1501.13,753.598C1497.99,755.086 1495.28,753.617 1495.28,753.617L1500.86,762.056Z" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.160219,-0.0758207,-0.0375643,-0.247596,12684.9,376.33)">
<path d="M1496.17,759.473L1555.54,720.014" style="fill:none;"/>
</g>
<g transform="matrix(0.160219,-0.0758207,-0.0375643,-0.247596,12684.9,376.33)">
<path d="M1496.1,754.362C1496.1,754.362 1497.2,755.607 1501.13,753.598C1503.25,752.509 1505.74,751.023 1508.49,749.329C1513.52,746.234 1519.35,742.314 1525.19,738.438C1530.13,735.166 1534.94,731.832 1539.27,728.802C1542.87,726.279 1549.36,722.059 1549.81,721.75C1552.75,719.73 1552.18,718.196 1552.18,718.196L1555.28,724.152C1555.28,724.152 1553.77,722.905 1551.37,724.681C1550.93,725.006 1544.52,729.349 1540.82,731.68C1536.38,734.479 1531.45,737.766 1526.52,741.049C1520.68,744.94 1514.82,748.79 1509.98,752.255C1507.33,754.151 1504.89,755.771 1503.09,757.456C1499.47,760.841 1501.26,763.283 1501.26,763.283L1496.1,754.362Z" style="fill:rgb(16,16,16);"/>
</g>
<g transform="matrix(0.156811,0,0,0.230771,12477,-400.567)">
<g transform="matrix(94.7456,0,0,94.7456,2811.73,2063)">
<path d="M0.449,-0.7L0.177,-0.7C0.185,-0.682 0.197,-0.654 0.2,-0.648C0.205,-0.639 0.216,-0.628 0.239,-0.628L0.32,-0.628C0.332,-0.628 0.336,-0.62 0.334,-0.611L0.128,-0L0.389,-0C0.412,-0 0.422,-0.01 0.427,-0.02L0.45,-0.071L0.255,-0.071C0.245,-0.071 0.239,-0.078 0.242,-0.09L0.449,-0.7Z" style="fill:rgb(16,16,16);fill-rule:nonzero;"/>
</g>
<g transform="matrix(94.7456,0,0,94.7456,2882.79,2063)">
<path d="M0.214,-0.7L0.214,-0.015C0.215,-0.01 0.218,-0 0.235,-0L0.286,-0L0.286,-0.672C0.286,-0.684 0.278,-0.7 0.257,-0.7L0.214,-0.7Z" style="fill:rgb(16,16,16);fill-rule:nonzero;"/>
</g>
<g transform="matrix(94.7456,0,0,94.7456,2944.37,2063)">
<path d="M0.441,-0.7L0.155,-0.7C0.143,-0.7 0.133,-0.69 0.133,-0.678L0.133,-0.629L0.234,-0.629L0.234,-0.015C0.234,-0.01 0.237,-0 0.254,-0L0.305,-0L0.305,-0.612C0.306,-0.621 0.313,-0.629 0.323,-0.629L0.379,-0.629C0.402,-0.629 0.413,-0.639 0.417,-0.648L0.441,-0.7Z" style="fill:rgb(16,16,16);fill-rule:nonzero;"/>
</g>
<g transform="matrix(94.7456,0,0,94.7456,3010.69,2063)">
<path d="M0.422,-0L0.343,-0L0.28,-0.482L0.217,-0L0.138,-0L0.244,-0.7L0.283,-0.7C0.313,-0.7 0.318,-0.681 0.321,-0.662L0.422,-0Z" style="fill:rgb(16,16,16);fill-rule:nonzero;"/>
</g>
<g transform="matrix(94.7456,0,0,94.7456,3077.96,2063)">
<path d="M0.186,-0.7L0.186,-0L0.325,-0C0.374,-0 0.413,-0.039 0.414,-0.088L0.414,-0.612C0.413,-0.661 0.374,-0.7 0.325,-0.7L0.186,-0.7ZM0.343,-0.108C0.343,-0.081 0.325,-0.071 0.305,-0.071L0.258,-0.071L0.258,-0.628L0.305,-0.628C0.325,-0.628 0.343,-0.618 0.343,-0.592L0.343,-0.108Z" style="fill:rgb(16,16,16);fill-rule:nonzero;"/>
</g>
<g transform="matrix(94.7456,0,0,94.7456,3149.02,2063)">
<path d="M0.291,-0.071L0.291,-0.314C0.291,-0.323 0.299,-0.331 0.308,-0.331L0.338,-0.331C0.361,-0.331 0.371,-0.341 0.376,-0.35C0.379,-0.356 0.391,-0.385 0.399,-0.403L0.291,-0.403L0.291,-0.611C0.291,-0.621 0.298,-0.628 0.308,-0.628L0.366,-0.628C0.389,-0.628 0.4,-0.639 0.404,-0.648L0.428,-0.7L0.241,-0.7C0.229,-0.7 0.22,-0.691 0.219,-0.68L0.219,-0L0.379,-0C0.402,-0 0.413,-0.01 0.418,-0.019C0.421,-0.025 0.433,-0.053 0.441,-0.071L0.291,-0.071Z" style="fill:rgb(16,16,16);fill-rule:nonzero;"/>
</g>
<g transform="matrix(94.7456,0,0,94.7456,3220.08,2063)">
<path d="M0.283,-0.071L0.283,-0.678C0.283,-0.69 0.273,-0.699 0.261,-0.7L0.211,-0.7L0.211,-0L0.383,-0C0.406,-0 0.417,-0.01 0.422,-0.019C0.425,-0.025 0.437,-0.053 0.445,-0.071L0.283,-0.071Z" style="fill:rgb(16,16,16);fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-41.5984,155.247,-155.247,-41.5984,201.516,76.8392)"><stop offset="0" style="stop-color:rgb(255,143,0);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(254,0,255);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(155.247,-41.5984,41.5984,155.247,110.08,195.509)"><stop offset="0" style="stop-color:rgb(255,143,0);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(254,0,255);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-113.649,-113.649,113.649,-113.649,258.31,215.618)"><stop offset="0" style="stop-color:rgb(255,143,0);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(254,0,255);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-155.247,41.5984,-41.5984,-155.247,220.914,144.546)"><stop offset="0" style="stop-color:rgb(255,143,0);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(254,0,255);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-113.649,113.649,113.649,113.649,206.837,124.661)"><stop offset="0" style="stop-color:rgb(255,143,0);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(254,0,255);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-41.5984,-155.247,-155.247,41.5984,152.054,262.8)"><stop offset="0" style="stop-color:rgb(255,143,0);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(254,0,255);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

2
docs/static/img/providers/zoho.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.2 KiB

60
docs/static/img/providers/zoom.svg vendored Normal file
View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="Zoom_2011.svg"
id="svg835"
version="1.1"
height="224.72337"
width="1000">
<metadata
id="metadata841">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs839" />
<sodipodi:namedview
inkscape:current-layer="svg835"
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="1912"
inkscape:cy="-314.24417"
inkscape:cx="948.1979"
inkscape:zoom="0.49497475"
showgrid="false"
id="namedview837"
inkscape:window-height="1017"
inkscape:window-width="1920"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
inkscape:document-rotation="0"
bordercolor="#666666"
pagecolor="#ffffff"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<path
inkscape:connector-curvature="0"
id="path833"
d="m 814.1856,67.388717 c 3.82247,6.59494 5.07303,14.0983 5.48595,22.53368 l 0.5427,11.243253 v 78.62013 l 0.55449,11.25504 c 1.10899,18.38088 14.66459,31.97188 33.18704,33.12806 l 11.19606,0.55449 V 101.16565 l 0.55449,-11.243253 c 0.46011,-8.341 1.69888,-15.98593 5.58033,-22.62806 a 44.902197,44.902197 0 0 1 77.74711,0.14157 c 3.82246,6.59494 5.01403,14.23987 5.47415,22.48649 l 0.55449,11.207863 v 78.65552 l 0.55449,11.25504 c 1.15618,18.47526 14.60561,32.06626 33.18705,33.12806 L 1000,224.72337 V 89.922397 A 89.898775,89.898775 0 0 0 910.13662,0.02362708 89.662821,89.662821 0 0 0 842.71254,30.473587 89.780798,89.780798 0 0 0 775.28845,0.01182708 c -18.66402,0 -35.9831,5.66292002 -50.32915,15.44323992 C 716.20538,5.6865371 696.62113,0.01182708 685.37788,0.01182708 V 224.72337 l 11.24325,-0.55449 c 18.80559,-1.23877 32.39659,-14.46403 33.12805,-33.12806 l 0.60168,-11.25504 v -78.62013 l 0.5545,-11.243253 c 0.47191,-8.48257 1.65168,-15.93874 5.48595,-22.58087 a 45.020174,45.020174 0 0 1 38.89714,-22.39211 44.949388,44.949388 0 0 1 38.89715,22.4393 z M 44.93759,224.18067 l 11.243246,0.5427 H 224.71155 l -0.5545,-11.20785 c -1.52191,-18.47526 -14.6056,-31.97188 -33.13985,-33.17525 l -11.24324,-0.55449 H 78.667327 L 213.4683,44.937617 212.91381,33.741567 C 212.04077,15.077537 198.40259,1.5927271 179.77396,0.56631708 l -11.24325,-0.5073 L 0,0.01182708 0.55449376,11.255077 C 2.0292112,29.553367 15.301668,43.333127 33.682546,44.394927 l 11.255044,0.55449 H 146.04422 L 11.243246,179.79758 11.79774,191.04082 c 1.108987,18.52245 14.475826,31.93648 33.13985,33.12806 z M 641.26613,32.903927 a 112.34987,112.34987 0 0 1 0,158.903753 112.43246,112.43246 0 0 1 -158.93914,0 c -43.8758,-43.8758 -43.8758,-115.027963 0,-158.903753 A 112.29089,112.29089 0 0 1 561.72577,2.7082708e-5 112.37347,112.37347 0 0 1 641.26613,32.915727 Z m -31.7949,31.8185 a 67.447677,67.447677 0 0 1 0,95.349333 67.447677,67.447677 0 0 1 -95.34934,0 67.447677,67.447677 0 0 1 0,-95.349333 67.447677,67.447677 0 0 1 95.34934,0 z M 325.91256,2.7082708e-5 A 112.29089,112.29089 0 0 1 405.31134,32.915727 c 43.88759,43.86399 43.88759,115.027953 0,158.891953 a 112.43246,112.43246 0 0 1 -158.93914,0 c -43.8758,-43.8758 -43.8758,-115.027963 0,-158.903753 A 112.29089,112.29089 0 0 1 325.77098,2.7082708e-5 Z M 373.51643,64.698837 a 67.447677,67.447677 0 0 1 0,95.361123 67.447677,67.447677 0 0 1 -95.34933,0 67.447677,67.447677 0 0 1 0,-95.349333 67.447677,67.447677 0 0 1 95.34933,0 z"
style="fill:#2d8cff;fill-rule:evenodd;stroke-width:11.79773998" />
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1 @@
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

View File

@@ -1,6 +1,6 @@
{
"name": "@next-auth/neo4j-adapter",
"version": "1.0.5",
"version": "1.0.6",
"description": "neo4j adapter for next-auth.",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
@@ -33,7 +33,7 @@
"dist"
],
"peerDependencies": {
"neo4j-driver": "^4.0.0",
"neo4j-driver": "^4.0.0 || ^5.7.0",
"next-auth": "^4"
},
"devDependencies": {
@@ -41,7 +41,7 @@
"@next-auth/tsconfig": "workspace:*",
"@types/uuid": "^8.3.3",
"jest": "^27.4.3",
"neo4j-driver": "^4.4.0",
"neo4j-driver": "^5.7.0",
"next-auth": "workspace:*"
},
"dependencies": {
@@ -50,4 +50,4 @@
"jest": {
"preset": "@next-auth/adapter-test/jest"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@next-auth/prisma-adapter",
"version": "1.0.6",
"version": "1.0.7",
"description": "Prisma adapter for next-auth.",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",

View File

@@ -42,8 +42,6 @@ import type { Adapter, AdapterAccount } from "next-auth/adapters"
* })
* ```
*
* ## Advanced usage
*
* ### Create the Prisma schema from scratch
*
* You need to use at least Prisma 2.26.0. Create a schema file in `prisma/schema.prisma` similar to this one:

View File

@@ -195,7 +195,7 @@ import type { XataClient } from "./xata"
* xata init --schema=./path/to/your/schema.json
* ```
*
* The CLI will walk you through a setup process where you choose a [workspace](https://docs.xata.io/concepts/workspaces) (kind of like a GitHub org or a Vercel team) and an appropriate database. We recommend using a fresh database for this, as we'll augment it with tables that Auth.js needs.
* The CLI will walk you through a setup process where you choose a [workspace](https://xata.io/docs/api-reference/workspaces) (kind of like a GitHub org or a Vercel team) and an appropriate database. We recommend using a fresh database for this, as we'll augment it with tables that Auth.js needs.
*
* Once you're done, you can continue using Auth.js in your project as expected, like creating a `./pages/api/auth/[...nextauth]` route.
*

View File

@@ -21,4 +21,4 @@
---
Check out the documentation at [authjs.dev](https://authjs.dev/reference/core/modules/main).
Check out the documentation at [authjs.dev](https://authjs.dev/reference/core).

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/core",
"version": "0.7.0",
"version": "0.8.0",
"description": "Authentication for the Web.",
"keywords": [
"authentication",
@@ -88,7 +88,7 @@
"@types/cookie": "0.5.1",
"@types/node": "18.11.10",
"@types/nodemailer": "6.4.6",
"@types/react": "18.0.26",
"@types/react": "18.0.37",
"autoprefixer": "10.4.13",
"postcss": "8.4.19",
"postcss-nested": "6.0.0"

View File

@@ -228,6 +228,10 @@ export interface Adapter {
deleteUser?(
userId: string
): Promise<void> | Awaitable<AdapterUser | null | undefined>
/**
* This method is invoked internally (but optionally can be used for manual linking).
* It creates an [Account](https://authjs.dev/reference/adapters#models) in the database.
*/
linkAccount?(
account: AdapterAccount
): Promise<void> | Awaitable<AdapterAccount | null | undefined>

View File

@@ -20,13 +20,6 @@ export class AuthError extends Error {
}
}
/**
* @todo
* Thrown when an Email address is already associated with an account
* but the user is trying an OAuth account that is not linked to it.
*/
export class AccountNotLinked extends AuthError {}
/**
* @todo
* One of the database `Adapter` methods failed.
@@ -37,8 +30,8 @@ export class AdapterError extends AuthError {}
export class AuthorizedCallbackError extends AuthError {}
/**
* There was an error while trying to finish up authenticating the user.
* Depending on the type of provider, this could be for multiple reasons.
* This error occurs when the user cannot finish the sign-in process.
* Depending on the provider type, this could have happened for multiple reasons.
*
* :::tip
* Check out `[auth][details]` in the error message to know which provider failed.
@@ -48,7 +41,7 @@ export class AuthorizedCallbackError extends AuthError {}
* ```
* :::
*
* For an **OAuth provider**, possible causes are:
* For an [OAuth provider](https://authjs.dev/reference/core/providers_oauth), possible causes are:
* - The user denied access to the application
* - There was an error parsing the OAuth Profile:
* Check out the provider's `profile` or `userinfo.request` method to make sure
@@ -56,7 +49,7 @@ export class AuthorizedCallbackError extends AuthError {}
* - The `signIn` or `jwt` callback methods threw an uncaught error:
* Check the callback method implementations.
*
* For an **Email provider**, possible causes are:
* For an [Email provider](https://authjs.dev/reference/core/providers_email), possible causes are:
* - The provided email/token combination was invalid/missing:
* Check if the provider's `sendVerificationRequest` method correctly sends the email.
* - The provided email/token combination has expired:
@@ -64,7 +57,7 @@ export class AuthorizedCallbackError extends AuthError {}
* - There was an error with the database:
* Check the database logs.
*
* For a **Credentials provider**, possible causes are:
* For a [Credentials provider](https://authjs.dev/reference/core/providers_credentials), possible causes are:
* - The `authorize` method threw an uncaught error:
* Check the provider's `authorize` method.
* - The `signIn` or `jwt` callback methods threw an uncaught error:
@@ -107,31 +100,87 @@ export class MissingAPIRoute extends AuthError {}
/** @todo */
export class MissingAuthorize extends AuthError {}
/** @todo */
/**
* Auth.js requires a secret to be set, but none was not found. This is used to encrypt cookies, JWTs and other sensitive data.
*
* :::note
* If you are using a framework like Next.js, we try to automatically infer the secret from the `AUTH_SECRET` environment variable.
* Alternatively, you can also explicitly set the [`AuthConfig.secret`](https://authjs.dev/reference/core#secret).
* :::
*
*
* :::tip
* You can generate a good secret value:
* - On Unix systems: type `openssl rand -hex 32` in the terminal
* - Or generate one [online](https://generate-secret.vercel.app/32)
*
* :::
*/
export class MissingSecret extends AuthError {}
/** @todo */
export class OAuthSignInError extends AuthError {}
/**
* @todo
* Thrown when an Email address is already associated with an account
* but the user is trying an OAuth account that is not linked to it.
*/
export class OAuthAccountNotLinked extends AuthError {}
/** @todo */
/**
* Thrown when an OAuth provider returns an error during the sign in process.
* This could happen for example if the user denied access to the application or there was a configuration error.
*
* For a full list of possible reasons, check out the specification [Authorization Code Grant: Error Response](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1)
*/
export class OAuthCallbackError extends AuthError {}
/** @todo */
export class OAuthCreateUserError extends AuthError {}
/** @todo */
/**
* This error occurs during an OAuth sign in attempt when the provdier's
* response could not be parsed. This could for example happen if the provider's API
* changed, or the [`OAuth2Config.profile`](https://authjs.dev/reference/core/providers_oauth#profile) method is not implemented correctly.
*/
export class OAuthProfileParseError extends AuthError {}
/** @todo */
export class SessionTokenError extends AuthError {}
/** @todo */
/**
* This error occurs when the user cannot initiate the sign-in process.
* Depending on the provider type, this could have happened for multiple reasons.
*
* :::tip
* Check out `[auth][details]` in the error message to know which provider failed.
* @example
* ```sh
* [auth][details]: { "provider": "github" }
* ```
* :::
*
* For an [OAuth provider](https://authjs.dev/reference/core/providers_oauth), possible causes are:
* - The Authorization Server is not compliant with the [OAuth 2.0 specifcation](https://www.ietf.org/rfc/rfc6749.html)
* Check the details in the error message.
* - A runtime error occurred in Auth.js. This should be reported as a bug.
*
* For an [Email provider](https://authjs.dev/reference/core/providers_email), possible causes are:
* - The email sent from the client is invalid, could not be normalized by [`EmailConfig.normalizeIdentifier`](https://authjs.dev/reference/core/providers_email#normalizeidentifier)
* - The provided email/token combination has expired:
* Ask the user to log in again.
* - There was an error with the database:
* Check the database logs.
*
*/
export class SignInError extends AuthError {}
/** @todo */
export class SignOutError extends AuthError {}
/** @todo */
/**
* Auth.js was requested to handle an operation that it does not support.
*
* See [`AuthAction`](https://authjs.dev/reference/core/types#authaction) for the supported actions.
*/
export class UnknownAction extends AuthError {}
/** @todo */

View File

@@ -190,7 +190,7 @@ export interface JWTEncodeParams<Payload = JWT> {
/**
* The maximum age of the Auth.js issued JWT in seconds.
*
* @default 30 * 24 * 30 * 60 // 30 days
* @default 30 * 24 * 60 * 60 // 30 days
*/
maxAge?: number
}
@@ -213,7 +213,7 @@ export interface JWTOptions {
/**
* The maximum age of the Auth.js issued JWT in seconds.
*
* @default 30 * 24 * 30 * 60 // 30 days
* @default 30 * 24 * 60 * 60 // 30 days
*/
maxAge: number
/** Override this method to control the Auth.js issued JWT encoding. */

View File

@@ -1,4 +1,4 @@
import { AccountNotLinked } from "../errors.js"
import { OAuthAccountNotLinked } from "../errors.js"
import { fromDate } from "./utils/date.js"
import type {
@@ -49,7 +49,7 @@ export async function handleLogin(
}
const profile = _profile as AdapterUser
const account = _account as AdapterAccount
let account = _account as AdapterAccount
const {
createUser,
@@ -122,113 +122,116 @@ export async function handleLogin(
})
return { session, user, isNewUser }
} else if (account.type === "oauth" || account.type === "oidc") {
// If signing in with OAuth account, check to see if the account exists already
const userByAccount = await getUserByAccount({
providerAccountId: account.providerAccountId,
provider: account.provider,
})
if (userByAccount) {
if (user) {
// If the user is already signed in with this account, we don't need to do anything
if (userByAccount.id === user.id) {
return { session, user, isNewUser }
}
// If the user is currently signed in, but the new account they are signing in
// with is already associated with another user, then we cannot link them
// and need to return an error.
throw new AccountNotLinked(
"The account is already associated with another user",
{ provider: account.provider }
)
}
// If there is no active session, but the account being signed in with is already
// associated with a valid user then create session to sign the user in.
session = useJwtSession
? {}
: await createSession({
sessionToken: generateSessionToken(),
userId: userByAccount.id,
expires: fromDate(options.session.maxAge),
})
}
return { session, user: userByAccount, isNewUser }
} else {
if (user) {
// If the user is already signed in and the OAuth account isn't already associated
// with another user account then we can go ahead and link the accounts safely.
await linkAccount({ ...account, userId: user.id })
await events.linkAccount?.({ user, account, profile })
// As they are already signed in, we don't need to do anything after linking them
// If signing in with OAuth account, check to see if the account exists already
const userByAccount = await getUserByAccount({
providerAccountId: account.providerAccountId,
provider: account.provider,
})
if (userByAccount) {
if (user) {
// If the user is already signed in with this account, we don't need to do anything
if (userByAccount.id === user.id) {
return { session, user, isNewUser }
}
// If the user is currently signed in, but the new account they are signing in
// with is already associated with another user, then we cannot link them
// and need to return an error.
throw new OAuthAccountNotLinked(
"The account is already associated with another user",
{ provider: account.provider }
)
}
// If there is no active session, but the account being signed in with is already
// associated with a valid user then create session to sign the user in.
session = useJwtSession
? {}
: await createSession({
sessionToken: generateSessionToken(),
userId: userByAccount.id,
expires: fromDate(options.session.maxAge),
})
// If the user is not signed in and it looks like a new OAuth account then we
// check there also isn't an user account already associated with the same
// email address as the one in the OAuth profile.
//
// This step is often overlooked in OAuth implementations, but covers the following cases:
//
// 1. It makes it harder for someone to accidentally create two accounts.
// e.g. by signin in with email, then again with an oauth account connected to the same email.
// 2. It makes it harder to hijack a user account using a 3rd party OAuth account.
// e.g. by creating an oauth account then changing the email address associated with it.
//
// It's quite common for services to automatically link accounts in this case, but it's
// better practice to require the user to sign in *then* link accounts to be sure
// someone is not exploiting a problem with a third party OAuth service.
//
// OAuth providers should require email address verification to prevent this, but in
// practice that is not always the case; this helps protect against that.
const userByEmail = profile.email
? await getUserByEmail(profile.email)
: null
if (userByEmail) {
const provider = options.provider as OAuthConfig<any>
if (provider?.allowDangerousEmailAccountLinking) {
// If you trust the oauth provider to correctly verify email addresses, you can opt-in to
// account linking even when the user is not signed-in.
user = userByEmail
} else {
// We end up here when we don't have an account with the same [provider].id *BUT*
// we do already have an account with the same email address as the one in the
// OAuth profile the user has just tried to sign in with.
//
// We don't want to have two accounts with the same email address, and we don't
// want to link them in case it's not safe to do so, so instead we prompt the user
// to sign in via email to verify their identity and then link the accounts.
throw new AccountNotLinked(
"Another account already exists with the same e-mail address",
{ provider: account.provider }
)
}
} else {
// If the current user is not logged in and the profile isn't linked to any user
// accounts (by email or provider account id)...
//
// If no account matching the same [provider].id or .email exists, we can
// create a new account for the user, link it to the OAuth account and
// create a new session for them so they are signed in with it.
const { id: _, ...newUser } = { ...profile, emailVerified: null }
user = await createUser(newUser)
}
await events.createUser?.({ user })
return { session, user: userByAccount, isNewUser }
} else {
const { provider: p } = options as InternalOptions<"oauth" | "oidc">
const { type, provider, providerAccountId, userId, ...tokenSet } = account
const defaults = { providerAccountId, provider, type, userId }
account = Object.assign(p.account(tokenSet) ?? {}, defaults)
if (user) {
// If the user is already signed in and the OAuth account isn't already associated
// with another user account then we can go ahead and link the accounts safely.
await linkAccount({ ...account, userId: user.id })
await events.linkAccount?.({ user, account, profile })
session = useJwtSession
? {}
: await createSession({
sessionToken: generateSessionToken(),
userId: user.id,
expires: fromDate(options.session.maxAge),
})
return { session, user, isNewUser: true }
// As they are already signed in, we don't need to do anything after linking them
return { session, user, isNewUser }
}
}
throw new Error("Unsupported account type")
// If the user is not signed in and it looks like a new OAuth account then we
// check there also isn't an user account already associated with the same
// email address as the one in the OAuth profile.
//
// This step is often overlooked in OAuth implementations, but covers the following cases:
//
// 1. It makes it harder for someone to accidentally create two accounts.
// e.g. by signin in with email, then again with an oauth account connected to the same email.
// 2. It makes it harder to hijack a user account using a 3rd party OAuth account.
// e.g. by creating an oauth account then changing the email address associated with it.
//
// It's quite common for services to automatically link accounts in this case, but it's
// better practice to require the user to sign in *then* link accounts to be sure
// someone is not exploiting a problem with a third party OAuth service.
//
// OAuth providers should require email address verification to prevent this, but in
// practice that is not always the case; this helps protect against that.
const userByEmail = profile.email
? await getUserByEmail(profile.email)
: null
if (userByEmail) {
const provider = options.provider as OAuthConfig<any>
if (provider?.allowDangerousEmailAccountLinking) {
// If you trust the oauth provider to correctly verify email addresses, you can opt-in to
// account linking even when the user is not signed-in.
user = userByEmail
} else {
// We end up here when we don't have an account with the same [provider].id *BUT*
// we do already have an account with the same email address as the one in the
// OAuth profile the user has just tried to sign in with.
//
// We don't want to have two accounts with the same email address, and we don't
// want to link them in case it's not safe to do so, so instead we prompt the user
// to sign in via email to verify their identity and then link the accounts.
throw new OAuthAccountNotLinked(
"Another account already exists with the same e-mail address",
{ provider: account.provider }
)
}
} else {
// If the current user is not logged in and the profile isn't linked to any user
// accounts (by email or provider account id)...
//
// If no account matching the same [provider].id or .email exists, we can
// create a new account for the user, link it to the OAuth account and
// create a new session for them so they are signed in with it.
const { id: _, ...newUser } = { ...profile, emailVerified: null }
user = await createUser(newUser)
}
await events.createUser?.({ user })
await linkAccount({ ...account, userId: user.id })
await events.linkAccount?.({ user, account, profile })
session = useJwtSession
? {}
: await createSession({
sessionToken: generateSessionToken(),
userId: user.id,
expires: fromDate(options.session.maxAge),
})
return { session, user, isNewUser: true }
}
}

View File

@@ -47,7 +47,7 @@ export async function AuthInternal<
case "providers":
return (await routes.providers(options.providers)) as any
case "session": {
const session = await routes.session(sessionStore, options)
const session = await routes.session({ sessionStore, options })
if (session.cookies) cookies.push(...session.cookies)
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return { ...session, cookies } as any
@@ -110,14 +110,10 @@ export async function AuthInternal<
if (
[
"Signin",
"OAuthSignin",
"OAuthCallback",
"OAuthCreateAccount",
"EmailCreateAccount",
"Callback",
"OAuthAccountNotLinked",
"EmailSignin",
"CredentialsSignin",
"SessionRequired",
].includes(error as string)
) {
@@ -181,6 +177,22 @@ export async function AuthInternal<
return { ...callback, cookies }
}
break
case "session": {
if (options.csrfTokenVerified) {
const session = await routes.session({
options,
sessionStore,
newSession: request.body?.data,
isUpdate: true,
})
if (session.cookies) cookies.push(...session.cookies)
return { ...session, cookies } as any
}
// If CSRF token is invalid, return a 400 status code
// we should not redirect to a page as this is an API route
return { status: 400, cookies }
}
default:
}
}

View File

@@ -3,6 +3,7 @@ import * as o from "oauth4webapi"
import { OAuthCallbackError, OAuthProfileParseError } from "../../errors.js"
import type {
Account,
InternalOptions,
LoggerInstance,
Profile,
@@ -88,11 +89,9 @@ export async function handleOAuth(
/** https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1 */
if (o.isOAuth2Error(codeGrantParams)) {
logger.debug("OAuthCallbackError", {
providerId: provider.id,
...codeGrantParams,
})
throw new OAuthCallbackError(codeGrantParams.error)
const cause = { providerId: provider.id, ...codeGrantParams }
logger.debug("OAuthCallbackError", cause)
throw new OAuthCallbackError("OAuth Provider returned an error", cause)
}
const codeVerifier = await checks.pkce.use(cookies, resCookies, options)
@@ -123,8 +122,8 @@ export async function handleOAuth(
throw new Error("TODO: Handle www-authenticate challenges as needed")
}
let profile: Profile = {}
let tokens: TokenSet
let profile: Profile
let tokens: TokenSet & Pick<Account, "expires_at">
if (provider.type === "oidc") {
const nonce = await checks.nonce.use(cookies, resCookies, options)
@@ -162,40 +161,51 @@ export async function handleOAuth(
(tokens as any).access_token
)
profile = await userinfoResponse.json()
} else {
throw new TypeError("No userinfo endpoint configured")
}
}
const profileResult = await getProfile(profile, provider, tokens, logger)
if (tokens.expires_in) {
tokens.expires_at =
Math.floor(Date.now() / 1000) + Number(tokens.expires_in)
}
return { ...profileResult, cookies: resCookies }
const profileResult = await getUserAndAccount(
profile,
provider,
tokens,
logger
)
return { ...profileResult, profile, cookies: resCookies }
}
/** Returns profile, raw profile and auth provider details */
async function getProfile(
/** Returns the user and account that is going to be created in the database. */
async function getUserAndAccount(
OAuthProfile: Profile,
provider: OAuthConfigInternal<any>,
tokens: TokenSet,
logger: LoggerInstance
) {
try {
const profile = await provider.profile(OAuthProfile, tokens)
profile.email = profile.email?.toLowerCase()
const user = await provider.profile(OAuthProfile, tokens)
user.email = user.email?.toLowerCase()
if (!profile.id) {
if (!user.id) {
throw new TypeError(
`Profile id is missing in ${provider.name} OAuth profile response`
`User id is missing in ${provider.name} OAuth profile response`
)
}
return {
profile,
user,
account: {
provider: provider.id,
type: provider.type,
providerAccountId: profile.id.toString(),
providerAccountId: user.id.toString(),
...tokens,
},
OAuthProfile,
}
} catch (e) {
// If we didn't get a response either there was a problem with the provider
@@ -206,6 +216,8 @@ async function getProfile(
// redirected back to the sign up page. We log the error to help developers
// who might be trying to debug this when configuring a new provider.
logger.debug("getProfile error details", OAuthProfile)
logger.error(new OAuthProfileParseError(e as Error))
logger.error(
new OAuthProfileParseError(e as Error, { provider: provider.id })
)
}
}

View File

@@ -11,7 +11,7 @@ const signinErrors: Record<
default: "Unable to sign in.",
signin: "Try signing in with a different account.",
oauthsignin: "Try signing in with a different account.",
oauthcallback: "Try signing in with a different account.",
oauthcallbackerror: "Try signing in with a different account.",
oauthcreateaccount: "Try signing in with a different account.",
emailcreateaccount: "Try signing in with a different account.",
callback: "Try signing in with a different account.",

View File

@@ -1,13 +1,16 @@
import { OAuthProfileParseError } from "../errors.js"
import { merge } from "./utils/merge.js"
import type {
AccountCallback,
OAuthConfig,
OAuthConfigInternal,
OAuthEndpointType,
OAuthUserConfig,
ProfileCallback,
Provider,
} from "../providers/index.js"
import type { AuthConfig, InternalProvider } from "../types.js"
import type { AuthConfig, InternalProvider, Profile } from "../types.js"
/**
* Adds `signinUrl` and `callbackUrl` to each provider
@@ -77,18 +80,51 @@ function normalizeOAuth(
checks,
userinfo,
profile: c.profile ?? defaultProfile,
account: c.account ?? defaultAccount,
}
}
function defaultProfile(profile: any) {
return {
id: profile.sub ?? profile.id,
name:
profile.name ?? profile.nickname ?? profile.preferred_username ?? null,
email: profile.email ?? null,
image: profile.picture ?? null,
}
/**
* Returns basic user profile from the userinfo response/`id_token` claims.
* @see https://authjs.dev/reference/adapters#user
* @see https://openid.net/specs/openid-connect-core-1_0.html#IDToken
* @see https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
*/
const defaultProfile: ProfileCallback<Profile> = (profile) => {
const id = profile.sub ?? profile.id
if (!id) throw new OAuthProfileParseError("Missing user id")
return stripUndefined({
id: id.toString(),
name: profile.name ?? profile.nickname ?? profile.preferred_username,
email: profile.email,
image: profile.picture,
})
}
/**
* Returns basic OAuth/OIDC values from the token response.
* @see https://www.ietf.org/rfc/rfc6749.html#section-5.1
* @see https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse
* @see https://authjs.dev/reference/adapters#account
*/
const defaultAccount: AccountCallback = (account) => {
return stripUndefined({
access_token: account.access_token,
id_token: account.id_token,
refresh_token: account.refresh_token,
expires_at: account.expires_at,
scope: account.scope,
token_type: account.token_type,
session_state: account.session_state,
})
}
function stripUndefined<T extends object>(o: T): T {
const result = {} as any
for (let [k, v] of Object.entries(o)) v !== undefined && (result[k] = v)
return result as T
}
function normalizeEndpoint(
e?: OAuthConfig<any>[OAuthEndpointType],
issuer?: string

View File

@@ -1,4 +1,8 @@
import { CallbackRouteError, Verification } from "../../errors.js"
import {
CallbackRouteError,
OAuthCallbackError,
Verification,
} from "../../errors.js"
import { handleLogin } from "../callback-handler.js"
import { handleOAuth } from "../oauth/callback.js"
import { handleState } from "../oauth/handle-state.js"
@@ -68,14 +72,18 @@ export async function callback(params: {
logger.debug("authorization result", authorizationResult)
const { profile, account, OAuthProfile } = authorizationResult
const {
user: userFromProvider,
account,
profile: OAuthProfile,
} = authorizationResult
// If we don't have a profile object then either something went wrong
// or the user cancelled signing in. We don't know which, so we just
// direct the user to the signin page for now. We could do something
// else in future.
// TODO: Handle user cancelling signin
if (!profile || !account || !OAuthProfile) {
if (!userFromProvider || !account || !OAuthProfile) {
return { redirect: `${url}/signin`, cookies }
}
@@ -83,7 +91,7 @@ export async function callback(params: {
// Attempt to get Profile from OAuth provider details before invoking
// signIn callback - but if no user object is returned, that is fine
// (that just means it's a new user signing in for the first time).
let userOrProfile = profile
let userByAccountOrFromProvider
if (adapter) {
const { getUserByAccount } = adapter
const userByAccount = await getUserByAccount({
@@ -91,11 +99,15 @@ export async function callback(params: {
provider: provider.id,
})
if (userByAccount) userOrProfile = userByAccount
if (userByAccount) userByAccountOrFromProvider = userByAccount
}
const unauthorizedOrError = await handleAuthorized(
{ user: userOrProfile, account, profile: OAuthProfile },
{
user: userByAccountOrFromProvider,
account,
profile: OAuthProfile,
},
options
)
@@ -104,7 +116,7 @@ export async function callback(params: {
// Sign user in
const { user, session, isNewUser } = await handleLogin(
sessionStore.value,
profile,
userFromProvider,
account,
options
)
@@ -122,6 +134,7 @@ export async function callback(params: {
account,
profile: OAuthProfile,
isNewUser,
trigger: isNewUser ? "signUp" : "signIn",
})
// Clear cookies if token is null
@@ -152,7 +165,7 @@ export async function callback(params: {
})
}
await events.signIn?.({ user, account, profile, isNewUser })
await events.signIn?.({ user, account, profile: OAuthProfile, isNewUser })
// Handle first logins on new accounts
// e.g. option to send users to a new account landing page on initial login
@@ -232,6 +245,7 @@ export async function callback(params: {
user: loggedInUser,
account,
isNewUser,
trigger: isNewUser ? "signUp" : "signIn",
})
// Clear cookies if token is null
@@ -328,6 +342,7 @@ export async function callback(params: {
// @ts-expect-error
account,
isNewUser: false,
trigger: "signIn",
})
// Clear cookies if token is null
@@ -360,8 +375,18 @@ export async function callback(params: {
cookies,
}
} catch (e) {
if (e instanceof OAuthCallbackError) {
logger.error(e)
// REVIEW: Should we expose original error= and error_description=
// Should we use a different name for error= then, since we already use it for all kind of errors?
url.searchParams.set("error", OAuthCallbackError.name)
url.pathname += "/signin"
return { redirect: url.toString(), cookies }
}
const error = new CallbackRouteError(e as Error, { provider: provider.id })
logger.debug("callback route error details", { method, query, body })
logger.error(error)
url.searchParams.set("error", CallbackRouteError.name)
url.pathname += "/error"

View File

@@ -6,10 +6,13 @@ import type { InternalOptions, ResponseInternal, Session } from "../../types.js"
import type { SessionStore } from "../cookie.js"
/** Return a session object filtered via `callbacks.session` */
export async function session(
sessionStore: SessionStore,
export async function session(params: {
options: InternalOptions
): Promise<ResponseInternal<Session | null>> {
sessionStore: SessionStore
isUpdate?: boolean
newSession?: any
}): Promise<ResponseInternal<Session | null>> {
const { options, sessionStore, newSession, isUpdate } = params
const {
adapter,
jwt,
@@ -33,23 +36,24 @@ export async function session(
try {
const decodedToken = await jwt.decode({ ...jwt, token: sessionToken })
const newExpires = fromDate(sessionMaxAge)
// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
const session = {
user: {
name: decodedToken?.name,
email: decodedToken?.email,
image: decodedToken?.picture,
},
expires: newExpires.toISOString(),
}
if (!decodedToken) throw new Error("Invalid JWT")
// @ts-expect-error
const token = await callbacks.jwt({ token: decodedToken })
const token = await callbacks.jwt({
token: decodedToken,
...(isUpdate && { trigger: "update" }),
session: newSession,
})
const newExpires = fromDate(sessionMaxAge)
if (token !== null) {
// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
const session = {
user: { name: token.name, email: token.email, image: token.picture },
expires: newExpires.toISOString(),
}
// @ts-expect-error
const newSession = await callbacks.session({ session, token })
@@ -125,14 +129,12 @@ export async function session(
// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
session: {
user: {
name: user.name,
email: user.email,
image: user.image,
},
user: { name: user.name, email: user.email, image: user.image },
expires: session.expires.toISOString(),
},
user,
newSession,
...(isUpdate ? { trigger: "update" } : {}),
})
// Return session payload as response

View File

@@ -55,8 +55,9 @@ export async function signin(
} catch (e) {
const error = new SignInError(e as Error, { provider: provider.id })
logger.error(error)
url.searchParams.set("error", error.name)
url.pathname += "/error"
const code = provider.type === "email" ? "EmailSignin" : "OAuthSignin"
url.searchParams.set("error", code)
url.pathname += "/signin"
return { redirect: url.toString() }
}
}

View File

@@ -33,6 +33,8 @@ export async function toInternalRequest(
// TODO: url.toString() should not include action and providerId
// see init.ts
const url = new URL(req.url.replace(/\/$/, ""))
// FIXME: Upstream issue in Next.js, pathname segments get included as part of the query string
url.searchParams.delete("nextauth")
const { pathname } = url
const action = actions.find((a) => pathname.includes(a))

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#fff", display: "flex", justifyContent: "space-between", color: "#000", padding: 16}}>
* <span>Built-in <b>42School</b> integration.</span>
* <a href="https://api.intra.42.fr//">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/42-school.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/42-school
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface UserData {
@@ -152,6 +162,57 @@ export interface FortyTwoProfile extends UserData, Record<string, any> {
user: any | null
}
/**
* Add 42School login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/42-school
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import 42School from "@auth/core/providers/42-school"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [42School({ clientId: 42_SCHOOL_CLIENT_ID, clientSecret: 42_SCHOOL_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [42School OAuth documentation](https://api.intra.42.fr/apidoc/guides/web_application_flow)
*
* ### Notes
*
*
* :::note
* 42 returns a field on `Account` called `created_at` which is a number. See the [docs](https://api.intra.42.fr/apidoc/guides/getting_started#make-basic-requests). Make sure to add this field to your database schema, in case if you are using an [Adapter](https://authjs.dev/reference/adapters).
* :::
* By default, Auth.js assumes that the 42School provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The 42School provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/42-school.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function FortyTwo<P extends FortyTwoProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -1,22 +1,19 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Apple</b> integration.</span>
* <a href="https://apple.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/apple-dark.svg" height="48" width="48"/>
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
* <span style={{fontSize: "1.35rem" }}>
* Built-in sign in with <b>Apple</b> integration.
* </span>
* <a href="https://apple.com" style={{backgroundColor: "black", padding: "12px", borderRadius: "100%" }}>
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/apple-dark.svg" width="24"/>
* </a>
* </div>
*
* ---
* @module providers/apple
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/**
* See more at:
* [Retrieve the User's Information from Apple ID Servers
](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773)
*/
/** The returned user profile from Apple when using the profile callback. */
export interface AppleProfile extends Record<string, any> {
/**
* The issuer registered claim identifies the principal that issued the identity token.
@@ -99,6 +96,51 @@ export interface AppleProfile extends Record<string, any> {
auth_time: number
}
/**
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/apple
* ```
*
* #### Configuration
*
* Import the provider and configure it in your **Auth.js** initialization file:
*
* ```ts title="pages/api/auth/[...nextauth].ts"
* import NextAuth from "next-auth"
* import AppleProvider from "next-auth/providers/apple"
*
* export default NextAuth({
* providers: [
* AppleProvider({
* clientId: process.env.GITHUB_ID,
* clientSecret: process.env.GITHUB_SECRET,
* }),
* ],
* })
* ```
*
* ### Resources
*
* - Sign in with Apple [Overview](https://developer.apple.com/sign-in-with-apple/get-started/)
* - Sign in with Apple [REST API](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api)
* - [How to retrieve](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773) the user's information from Apple ID servers
* - [Learn more about OAuth](https://authjs.dev/concepts/oauth)
* ### Notes
*
* The Apple provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/apple.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* ## Help
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*/
export default function Apple<P extends AppleProfile>(
options: Omit<OAuthUserConfig<P>, "clientSecret"> & {
/**

View File

@@ -1,95 +1,107 @@
/**
* <div style={{backgroundColor: "#24292f", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Asgardeo</b> integration.</span>
* <a href="https://wso2.com/asgardeo/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/asgardeo-dark.svg" height="48" width="48"/>
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
* <span style={{fontSize: "1.35rem" }}>
* Built-in sign in with <b>Asgardeo</b> integration.
* </span>
* <a href="https://wso2.com/asgardeo/" style={{backgroundColor: "#ECEFF1", padding: "12px", borderRadius: "100%" }}>
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/asgardeo-dark.svg" width="24"/>
* </a>
* </div>
*
* ---
* @module providers/asgardeo
*/
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
export interface AsgardeoProfile {
/** The returned user profile from Asgardeo when using the profile callback. */
export interface AsgardeoProfile extends Record<string, any> {
/**
* The user Asgardeo account ID
*/
sub: string
/**
* The user name
*/
given_name: string
/**
* The user email
*/
email: string
/**
* The user profile picture
*/
picture: string
}
/**
* Add Asgardeo login to your page.
* ## Documentation
*
* https://wso2.com/asgardeo/docs/guides/authentication
*
*
* ## Instructions
*
* - Log into https://console.asgardeo.io.
* - Next, go to "Application" tab (More info: https://wso2.com/asgardeo/docs/guides/applications/register-oidc-web-app/).
* - Register standard based - Open id connect, application.
* - Add callback URL: http://localhost:3000/api/auth/callback/asgardeo and https://your-domain.com/api/auth/callback/asgardeo
* - After registering the application, go to protocol tab.
* - Check `code` grant type.
* - Add Authorized redirect URLs & Allowed origins fields.
* - Make Email, First Name, Photo URL user attributes mandatory from the console.
*
* Create a `.env` file in the project root add the following entries:
*
* These values can be collected from the application created.
* ### Setup
*
* #### Callback URL
* ```
* ASGARDEO_CLIENT_ID=<Copy client ID from protocol tab here>
* ASGARDEO_CLIENT_SECRET=<Copy client from protocol tab here>
* ASGARDEO_ISSUER=<Copy the issuer url from the info tab here>
* https://example.com/api/auth/callback/asgardeo
* ```
*
* In `pages/api/auth/[...nextauth].js` find or add the `Asgardeo` entries:
* #### Configuration
*
* ```js
* import Asgardeo from "next-auth/providers/asgardeo";
* ...
* providers: [
* Asgardeo({
* clientId: process.env.ASGARDEO_CLIENT_ID,
* clientSecret: process.env.ASGARDEO_CLIENT_SECRET,
* issuer: process.env.ASGARDEO_ISSUER
* }),
* ],
* Import the provider and configure it in your **Auth.js** initialization file:
*
* ...
* ```ts title="pages/api/auth/[...nextauth].ts"
* import NextAuth from "next-auth"
* import AsgardeoProvider from "next-auth/providers/asgardeo";
*
* export default NextAuth({
* providers: [
* AsgardeoProvider({
* clientId: process.env.ASGARDEO_CLIENT_ID,
* clientSecret: process.env.ASGARDEO_CLIENT_SECRET,
* issuer: process.env.ASGARDEO_ISSUER
* }),
* ],
* })
* ```
*
* ## Resources
* ### Configuring Asgardeo
*
* @see [Asgardeo - Authentication Guide](https://wso2.com/asgardeo/docs/guides/authentication)
* @see [Learn more about OAuth](https://authjs.dev/concepts/oauth)
* @see [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts)
* Follow these steps:
*
* ## Notes
* 1. Log into the [Asgardeo console](https://console.asgardeo.io)
* 2. Next, go to "Application" tab (more info [here](https://wso2.com/asgardeo/docs/guides/applications/register-oidc-web-app/))
* 3. Register a standard based, Open ID connect, application
* 4. Add the **callback URLs**: `http://localhost:3000/api/auth/callback/asgardeo` (development) and `https://{YOUR_DOMAIN}.com/api/auth/callback/asgardeo` (production)
* 5. After registering the application, go to "Protocol" tab.
* 6. Check `code` as the grant type.
* 7. Add "Authorized redirect URLs" & "Allowed origins fields"
* 8. Make Email, First Name, Photo URL user attributes mandatory from the console.
*
* By default, Auth.js assumes that the Asgardeo provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
* Then, create a `.env` file in the project root add the following entries:
*
* :::tip
* ```
* ASGARDEO_CLIENT_ID="Copy client ID from protocol tab here"
* ASGARDEO_CLIENT_SECRET="Copy client from protocol tab here"
* ASGARDEO_ISSUER="Copy the issuer url from the info tab here"
* ```
*
* The Asgardeo provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
* ### Resources
*
* - [Asgardeo - Authentication Guide](https://wso2.com/asgardeo/docs/guides/authentication)
* - [Learn more about OAuth](https://authjs.dev/concepts/oauth)
*
* ### Notes
*
* The Asgardeo provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::info
* By default, Auth.js assumes that the Asgardeo provider is based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) spec
* :::
*
* :::info **Disclaimer**
* ## Help
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Asgardeo(
config: OIDCUserConfig<AsgardeoProfile>

View File

@@ -1,12 +1,79 @@
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
* <span style={{fontSize: "1.35rem" }}>
* Built-in sign in with <b>Atlassian</b> integration.
* </span>
* <a href="https://www.atlassian.com/" style={{backgroundColor: "black", padding: "12px", borderRadius: "100%" }}>
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/atlassian.svg" width="24" style={{ marginTop: "-3px"}} />
* </a>
* </div>
*
* @module providers/atlassian
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
interface AtlassianProfile extends Record<string, any> {
/** The returned user profile from Atlassian when using the profile callback. */
export interface AtlassianProfile extends Record<string, any> {
/**
* The user's atlassian account ID
*/
account_id: string
/**
* The user name
*/
name: string
/**
* The user's email
*/
email: string
/**
* The user's profile picture
*/
picture: string
}
/**
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/atlassian
* ```
*
* #### Configuration
*
* Import the provider and configure it in your **Auth.js** initialization file:
*
* ```ts title="pages/api/auth/[...nextauth].ts"
* import NextAuth from "next-auth"
* import AtlassianProvider from "next-auth/providers/atlassian"
*
* export default NextAuth({
* providers: [
* AtlassianProvider({
* clientId: process.env.ATLASSIAN_ID,
* clientSecret: process.env.ATLASSIAN_SECRET,
* }),
* ],
* })
* ```
*
* ### Resources
*
* - [Atlassian docs](https://developer.atlassian.com/server/jira/platform/oauth/)
*
* ### Notes
*
* The Atlassian provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/atlassian.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* ## Help
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*/
export default function Atlassian<P extends AtlassianProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -1,19 +1,19 @@
/**
* <div style={{backgroundColor: "#EB5424", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Auth0</b> integration.</span>
* <a href="https://auth0.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/auth0-dark.svg" height="48" width="48"/>
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
* <span style={{fontSize: "1.35rem" }}>
* Built-in sign in with <b>Auth0</b> integration.
* </span>
* <a href="https://auth0.com" style={{backgroundColor: "black", padding: "12px", borderRadius: "100%" }}>
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/auth0-dark.svg" width="24"/>
* </a>
* </div>
*
* ---
* @module providers/auth0
*/
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
/** @see [User Profile Structure](https://auth0.com/docs/manage-users/user-accounts/user-profiles/user-profile-structure) */
export interface Auth0Profile {
/** The returned user profile from Auth0 when using the profile callback. [Reference](https://auth0.com/docs/manage-users/user-accounts/user-profiles/user-profile-structure). */
export interface Auth0Profile extends Record<string, any> {
/** The user's unique identifier. */
sub: string
/** Custom fields that store info about a user that influences the user's access, such as support plan, security roles (if not using the Authorization Core feature set), or access control groups. To learn more, read Metadata Overview. */
@@ -75,51 +75,47 @@ export interface Auth0Profile {
}
/**
* Add Auth0 login to your page.
* ### Setup
*
* ## Example
* #### Callback URL
* ```
* https://example.com/api/auth/callback/auth0
* ```
*
* ```ts
* import { Auth } from "@auth/core"
* import Auth0 from "@auth/core/providers/auth0"
* #### Configuration
*
* const request = new Request("https://example.com")
* const response = await Auth(request, {
* providers: [Auth0({ clientId: "", clientSecret: "", issuer: "" })],
* Import the provider and configure it in your **Auth.js** initialization file:
*
* ```ts title="pages/api/auth/[...nextauth].ts"
* import NextAuth from "next-auth"
* import Auth0Provider from "next-auth/providers/auth0"
*
* export default NextAuth({
* providers: [
* Auth0Provider({
* clientId: process.env.AUTH0_ID,
* clientSecret: process.env.AUTH0_SECRET,
* }),
* ],
* })
* ```
*
* ---
* ### Resources
*
* ## Resources
* - [Auth0 docs](https://auth0.com/docs/authenticate)
*
* - [Authenticate - Auth0 docs](https://auth0.com/docs/authenticate)
* ### Notes
*
* ---
* The Auth0 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/auth0.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* ## Notes
*
* By default, Auth.js assumes that the Auth0 provider is
* based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.
*
* :::tip
*
* The Auth0 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/auth0.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
* ## Help
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Auth0(
config: OIDCUserConfig<Auth0Profile>
): OIDCConfig<Auth0Profile> {

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#fd4b2d", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Authentik</b> integration.</span>
* <a href="https://goauthentik.io/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/authentik.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/authentik
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface AuthentikProfile extends Record<string, any> {
@@ -21,6 +31,57 @@ export interface AuthentikProfile extends Record<string, any> {
groups: string[]
}
/**
* Add Authentik login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/authentik
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Authentik from "@auth/core/providers/authentik"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Authentik({ clientId: AUTHENTIK_CLIENT_ID, clientSecret: AUTHENTIK_CLIENT_SECRET, issuer: AUTHENTIK_ISSUER })],
* })
* ```
*
* :::note
* issuer should include the slug without a trailing slash e.g., https://my-authentik-domain.com/application/o/My_Slug
* :::
*
* ### Resources
*
* - [Authentik OAuth documentation](https://goauthentik.io/docs/providers/oauth2)
*
* ### Notes
*
* By default, Auth.js assumes that the Authentik provider is
* based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.
*
* :::tip
*
* The Authentik provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/authentik.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Authentik<P extends AuthentikProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -6,7 +6,6 @@
* </a>
* </div>
*
* ---
* @module providers/azure-ad-b2c
*/
@@ -60,7 +59,7 @@ export interface AzureADB2CProfile {
* - Identity Provider Access Token
* - User's Object ID
*
* ## Example
* @example
*
* ```ts
* import { Auth } from "@auth/core"
@@ -75,13 +74,13 @@ export interface AzureADB2CProfile {
*
* ---
*
* ## Resources
* ### Resources
*
* - [Azure Active Directory B2C documentation](https://learn.microsoft.com/en-us/azure/active-directory-b2c)
*
* ---
*
* ## Notes
* ### Notes
*
* By default, Auth.js assumes that the Azure AD B2C provider is
* based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#0072c6", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Azure AD</b> integration.</span>
* <a href="https://learn.microsoft.com/en-us/azure/active-directory">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/azure-dark.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/azure-ad
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface AzureADProfile extends Record<string, any> {
@@ -7,6 +17,101 @@ export interface AzureADProfile extends Record<string, any> {
picture: string
}
/**
* Add AzureAd login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/azure-ad
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import AzureAd from "@auth/core/providers/azure-ad"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [AzureAd({ clientId: AZURE_AD_CLIENT_ID, clientSecret: AZURE_AD_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [AzureAd OAuth documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow/)
* - [AzureAd OAuth apps](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app/)
*
* @example
*
* ### To allow specific Active Directory users access:
*
* - In https://portal.azure.com/ search for "Azure Active Directory", and select your organization.
* - Next, go to "App Registration" in the left menu, and create a new one.
* - Pay close attention to "Who can use this application or access this API?"
* - This allows you to scope access to specific types of user accounts
* - Only your tenant, all azure tenants, or all azure tenants and public Microsoft accounts (Skype, Xbox, Outlook.com, etc.)
* - When asked for a redirection URL, use `https://yourapplication.com/api/auth/callback/azure-ad` or for development `http://localhost:3000/api/auth/callback/azure-ad`.
* - After your App Registration is created, under "Client Credential" create your Client secret.
* - Now copy your:
* - Application (client) ID
* - Directory (tenant) ID
* - Client secret (value)
*
* In `.env.local` create the following entries:
*
* ```
* AZURE_AD_CLIENT_ID=<copy Application (client) ID here>
* AZURE_AD_CLIENT_SECRET=<copy generated client secret value here>
* AZURE_AD_TENANT_ID=<copy the tenant id here>
* ```
*
* That will default the tenant to use the `common` authorization endpoint. [For more details see here](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints).
*
* :::note
* Azure AD returns the profile picture in an ArrayBuffer, instead of just a URL to the image, so our provider converts it to a base64 encoded image string and returns that instead. See: https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples. The default image size is 48x48 to avoid [running out of space](https://next-auth.js.org/faq#:~:text=What%20are%20the%20disadvantages%20of%20JSON%20Web%20Tokens%3F) in case the session is saved as a JWT.
* :::
*
* In `pages/api/auth/[...nextauth].js` find or add the `AzureAD` entries:
*
* ```js
* import AzureADProvider from "next-auth/providers/azure-ad";
*
* ...
* providers: [
* AzureADProvider({
* clientId: process.env.AZURE_AD_CLIENT_ID,
* clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
* tenantId: process.env.AZURE_AD_TENANT_ID,
* }),
* ]
* ...
*
* ```
*
* ### Notes
*
* By default, Auth.js assumes that the AzureAd provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The AzureAd provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/azure-ad.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function AzureAD<P extends AzureADProfile>(
options: OAuthUserConfig<P> & {
/**

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Battle.net</b> integration.</span>
* <a href="https://Battle.net/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/battlenet.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/battlenet
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface BattleNetProfile extends Record<string, any> {
@@ -10,6 +20,62 @@ export type BattleNetIssuer =
| "https://www.battlenet.com.cn/oauth"
| `https://${"us" | "eu" | "kr" | "tw"}.battle.net/oauth`
/**
* Add Battle.net login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/battlenet
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import BattleNet from "@auth/core/providers/battlenet"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [BattleNet({ clientId: BATTLENET_CLIENT_ID, clientSecret: BATTLENET_CLIENT_SECRET. issuer: BATTLENET_ISSUER })],
* })
* ```
* issuer must be one of these values, based on the available regions:
* ```
* type BattleNetIssuer =
* | "https://www.battlenet.com.cn/oauth"
* | "https://us.battle.net/oauth"
* | "https://eu.battle.net/oauth"
* | "https://kr.battle.net/oauth"
* | "https://tw.battle.net/oauth"
* ```
*
* ### Resources
*
* - [BattleNet OAuth documentation](https://develop.battle.net/documentation/guides/using-oauth)
*
* ### Notes
*
* By default, Auth.js assumes that the BattleNet provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The BattleNet provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/battlenet.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function BattleNet<P extends BattleNetProfile>(
options: OAuthUserConfig<P> & { issuer: BattleNetIssuer }
): OAuthConfig<P> {

View File

@@ -6,7 +6,6 @@
* </a>
* </div>
*
* ---
* @module providers/beyondidentity
*/
@@ -27,27 +26,27 @@ export interface BeyondIdentityProfile {
/**
* Add Beyond Identity login to your page.
*
* ## Example
* @example
*
* ```ts
* import { Auth } from "@auth/core"
* import BeyondIdentity from "@auth/core/providers/beyondidentity"
*
* const request = new Request("https://example.com")
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [BeyondIdentity({ clientId: "", clientSecret: "", issuer: "" })],
* providers: [BeyondIdentity({ clientId: BEYOND_IDENTITY_CLIENT_ID, clientSecret: BEYOND_IDENTITY_CLIENT_SECRET, issuer: BEYOND_IDENTITY_ISSUER })],
* })
* ```
*
* ---
*
* ## Resources
* ### Resources
*
* - [Beyond Identity Developer Docs](https://developer.beyondidentity.com/)
*
* ---
*
* ## Notes
* ### Notes
*
* By default, Auth.js assumes that the BeyondIdentity provider is
* based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.

View File

@@ -1,28 +0,0 @@
/** @type {import(".").OAuthProvider} */
export default function Box(options) {
return {
id: "box",
name: "Box",
type: "oauth",
authorization: "https://account.box.com/api/oauth2/authorize",
token: "https://api.box.com/oauth2/token",
userinfo: "https://api.box.com/2.0/users/me",
profile(profile) {
return {
id: profile.id,
name: profile.name,
email: profile.login,
image: profile.avatar_url,
}
},
style: {
logo: "/box.svg",
logoDark: "/box-dark.svg",
bg: "#fff",
text: "#0075C9",
bgDark: "#0075C9",
textDark: "#fff",
},
options,
}
}

View File

@@ -0,0 +1,89 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Box</b> integration.</span>
* <a href="https://box.com/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/box.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/box
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/**
* Add Box login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/box
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Box from "@auth/core/providers/box"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Box({ clientId: BOX_CLIENT_ID, clientSecret: BOX_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [Box developers documentation](https://developer.box.com/reference/)
* - [Box OAuth documentation](https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-box/)
*
* ### Notes
*
* By default, Auth.js assumes that the Box provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Box provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/box.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Box(
options: OAuthUserConfig<Record<string, any>>
): OAuthConfig<Record<string, any>> {
return {
id: "box",
name: "Box",
type: "oauth",
authorization: "https://account.box.com/api/oauth2/authorize",
token: "https://api.box.com/oauth2/token",
userinfo: "https://api.box.com/2.0/users/me",
profile(profile) {
return {
id: profile.id,
name: profile.name,
email: profile.login,
image: profile.avatar_url,
}
},
style: {
logo: "/box.svg",
logoDark: "/box-dark.svg",
bg: "#fff",
text: "#0075C9",
bgDark: "#0075C9",
textDark: "#fff",
},
options,
}
}

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>BoxyHQ SAML</b> integration.</span>
* <a href="https://boxyhq.com/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/boxyhq-saml.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/boxyhq-saml
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface BoxyHQSAMLProfile extends Record<string, any> {
@@ -7,6 +17,82 @@ export interface BoxyHQSAMLProfile extends Record<string, any> {
lastName?: string
}
/**
* Add BoxyHQ SAML login to your page.
*
* BoxyHQ SAML is an open source service that handles the SAML login flow as an OAuth 2.0 flow, abstracting away all the complexities of the SAML protocol.
*
* You can deploy BoxyHQ SAML as a separate service or embed it into your app using our NPM library. [Check out the documentation for more details](https://boxyhq.com/docs/jackson/deploy)
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/boxyhq-saml
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import BoxyHQ from "@auth/core/providers/boxyhq-saml"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [BoxyHQ({ clientId: BOXYHQ_SAML_CLIENT_ID, clientSecret: BOXYHQ_SAML_CLIENT_SECRET. issuer: BOXYHQ_SAML_ISSUER })],
* })
* ```
*
* ### Resources
*
* - [BoxyHQ OAuth documentation](https://example.com)
*
* ## Configuration
*
* SAML login requires a configuration for every tenant of yours. One common method is to use the domain for an email address to figure out which tenant they belong to. You can also use a unique tenant ID (string) from your backend for this, typically some kind of account or organization ID.
*
* Check out the [documentation](https://boxyhq.com/docs/jackson/saml-flow#2-saml-config-api) for more details.
*
*
* On the client side you'll need to pass additional parameters `tenant` and `product` to the `signIn` function. This will allow BoxyHQL SAML to figure out the right SAML configuration and take your user to the right SAML Identity Provider to sign them in.
*
* ```tsx
* import { signIn } from "next-auth/react";
* ...
*
* // Map your users's email to a tenant and product
* const tenant = email.split("@")[1];
* const product = 'my_awesome_product';
* ...
* <Button
* onClick={async (event) => {
* event.preventDefault();
*
* signIn("boxyhq-saml", {}, { tenant, product });
* }}>
* ...
* ```
* ### Notes
*
* By default, Auth.js assumes that the BoxyHQ provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The BoxyHQ provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/boxyhq-saml.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function SAMLJackson<P extends BoxyHQSAMLProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -1,25 +0,0 @@
/** @type {import(".").OAuthProvider} */
export default function Bungie(options) {
return {
id: "bungie",
name: "Bungie",
type: "oauth",
authorization: "https://www.bungie.net/en/OAuth/Authorize?reauth=true",
token: "https://www.bungie.net/platform/app/oauth/token/",
userinfo:
"https://www.bungie.net/platform/User/GetBungieAccount/{membershipId}/254/",
profile(profile) {
const { bungieNetUser: user } = profile.Response
return {
id: user.membershipId,
name: user.displayName,
email: null,
image: `https://www.bungie.net${
user.profilePicturePath.startsWith("/") ? "" : "/"
}${user.profilePicturePath}`,
}
},
options,
}
}

View File

@@ -0,0 +1,186 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Bungie</b> integration.</span>
* <a href="https://bungie.net/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/bungie.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/bungie
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/**
* Add Bungie login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/bungie
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Bungie from "@auth/core/providers/bungie"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Bungie({ clientId: BUNGIE_CLIENT_ID, clientSecret: BUNGIE_CLIENT_SECRET, headers: { "X-API-Key": BUNGIE_API_KEY } })],
* })
* ```
*
* ### Resources
*
* - [Bungie OAuth documentation](https://github.com/Bungie-net/api/wiki/OAuth-Documentation)
*
* ## Configuration
*
* :::tip
* Bungie require all sites to run HTTPS (including local development instances).
* :::
*
* :::tip
* Bungie doesn't allow you to use localhost as the website URL, instead you need to use https://127.0.0.1:3000
* :::
*
* Navigate to https://www.bungie.net/en/Application and fill in the required details:
*
* - Application name
* - Application Status
* - Website
* - OAuth Client Type
* - Confidential
* - Redirect URL
* - https://localhost:3000/api/auth/callback/bungie
* - Scope
* - `Access items like your Bungie.net notifications, memberships, and recent Bungie.Net forum activity.`
* - Origin Header
*
* The following guide may be helpful:
*
* - [How to setup localhost with HTTPS with a Next.js app](https://medium.com/@anMagpie/secure-your-local-development-server-with-https-next-js-81ac6b8b3d68)
*
* #@example server
*
* You will need to edit your host file and point your site at `127.0.0.1`
*
* [How to edit my host file?](https://phoenixnap.com/kb/how-to-edit-hosts-file-in-windows-mac-or-linux)
*
* On Windows (Run Powershell as administrator)
*
* ```ps
* Add-Content -Path C:\Windows\System32\drivers\etc\hosts -Value "127.0.0.1`tdev.example.com" -Force
* ```
*
* ```
* 127.0.0.1 dev.example.com
* ```
*
* ### Create certificate
*
* Creating a certificate for localhost is easy with openssl. Just put the following command in the terminal. The output will be two files: localhost.key and localhost.crt.
*
* ```bash
* openssl req -x509 -out localhost.crt -keyout localhost.key \
* -newkey rsa:2048 -nodes -sha256 \
* -subj "/CN=localhost" -extensions EXT -config <( \
* printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
* ```
*
* :::tip
* **Windows**
*
* The OpenSSL executable is distributed with [Git](https://git-scm.com/download/win]9) for Windows.
* Once installed you will find the openssl.exe file in `C:/Program Files/Git/mingw64/bin` which you can add to the system PATH environment variable if its not already done.
*
* Add environment variable `OPENSSL_CONF=C:/Program Files/Git/mingw64/ssl/openssl.cnf`
*
* ```bash
* req -x509 -out localhost.crt -keyout localhost.key \
* -newkey rsa:2048 -nodes -sha256 \
* -subj "/CN=localhost"
* ```
*
* :::
*
* Create directory `certificates` and place `localhost.key` and `localhost.crt`
*
* You can create a `server.js` in the root of your project and run it with `node server.js` to test Sign in with Bungie integration locally:
*
* ```js
* const { createServer } = require("https")
* const { parse } = require("url")
* const next = require("next")
* const fs = require("fs")
*
* const dev = process.env.NODE_ENV !== "production"
* const app = next({ dev })
* const handle = app.getRequestHandler()
*
* const httpsOptions = {
* key: fs.readFileSync("./certificates/localhost.key"),
* cert: fs.readFileSync("./certificates/localhost.crt"),
* }
*
* app.prepare().then(() => {
* createServer(httpsOptions, (req, res) => {
* const parsedUrl = parse(req.url, true)
* handle(req, res, parsedUrl)
* }).listen(3000, (err) => {
* if (err) throw err
* console.log("> Ready on https://localhost:3000")
* })
* })
* ```
*
*
* ### Notes
*
* By default, Auth.js assumes that the Bungie provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Bungie provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/bungie.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Bungie(
options: OAuthUserConfig<Record<string, any>>
): OAuthConfig<Record<string, any>> {
return {
id: "bungie",
name: "Bungie",
type: "oauth",
authorization: "https://www.bungie.net/en/OAuth/Authorize?reauth=true",
token: "https://www.bungie.net/platform/app/oauth/token/",
userinfo:
"https://www.bungie.net/platform/User/GetBungieAccount/{membershipId}/254/",
profile(profile) {
const { bungieNetUser: user } = profile.Response
return {
id: user.membershipId,
name: user.displayName,
email: null,
image: `https://www.bungie.net${
user.profilePicturePath.startsWith("/") ? "" : "/"
}${user.profilePicturePath}`,
}
},
options,
}
}

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Cognito</b> integration.</span>
* <a href="https://docs.aws.amazon.com/cognito">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/cognito.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/cognito
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface CognitoProfile extends Record<string, any> {
@@ -7,6 +17,62 @@ export interface CognitoProfile extends Record<string, any> {
picture: string
}
/**
* Add Cognito login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/cognito
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Cognito from "@auth/core/providers/cognito"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Cognito({ clientId: COGNITO_CLIENT_ID, clientSecret: COGNITO_CLIENT_SECRET, issuer: COGNITO_ISSUER })],
* })
* ```
*
* ### Resources
*
* - [Cognito OAuth documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html)
*
* ### Notes
* You need to select your AWS region to go the the Cognito dashboard.
*
* :::tip
* The issuer is a URL, that looks like this: https://cognito-idp.{region}.amazonaws.com/{PoolId}
* :::
* `PoolId` is from General Settings in Cognito, not to be confused with the App Client ID.
* :::warning
* Make sure you select all the appropriate client settings or the OAuth flow will not work.
* :::
*
* By default, Auth.js assumes that the Cognito provider is
* based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.
*
* :::tip
*
* The Cognito provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/cognito.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Cognito<P extends CognitoProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -1,21 +0,0 @@
/** @type {import(".").OAuthProvider} */
export default function Coinbase(options) {
return {
id: "coinbase",
name: "Coinbase",
type: "oauth",
authorization:
"https://www.coinbase.com/oauth/authorize?scope=wallet:user:email+wallet:user:read",
token: "https://api.coinbase.com/oauth/token",
userinfo: "https://api.coinbase.com/v2/user",
profile(profile) {
return {
id: profile.data.id,
name: profile.data.name,
email: profile.data.email,
image: profile.data.avatar_url,
}
},
options,
}
}

View File

@@ -0,0 +1,85 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Coinbase</b> integration.</span>
* <a href="https://coinbase.com/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/coinbase.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/coinbase
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/**
* Add Coinbase login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/coinbase
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Coinbase from "@auth/core/providers/coinbase"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Coinbase({ clientId: COINBASE_CLIENT_ID, clientSecret: COINBASE_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [Coinbase OAuth documentation](https://developers.coinbase.com/api/v2)
*
* ### Notes
*
* :::tip
* This Provider template has a 2 hour access token to it. A refresh token is also returned.
* :::
*
* By default, Auth.js assumes that the Coinbase provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Coinbase provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/coinbase.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Coinbase(
options: OAuthUserConfig<Record<string, any>>
): OAuthConfig<Record<string, any>> {
return {
id: "coinbase",
name: "Coinbase",
type: "oauth",
authorization:
"https://www.coinbase.com/oauth/authorize?scope=wallet:user:email+wallet:user:read",
token: "https://api.coinbase.com/oauth/token",
userinfo: "https://api.coinbase.com/v2/user",
profile(profile) {
return {
id: profile.data.id,
name: profile.data.name,
email: profile.data.email,
image: profile.data.avatar_url,
}
},
options,
}
}

View File

@@ -1,6 +1,6 @@
import type { CommonProviderOptions } from "./index.js"
import type { Awaitable, User } from "../types.js"
import type { JSXInternal } from "preact/src/jsx.js"
import type { JSX } from "preact"
/**
* Besides providing type safety inside {@link CredentialsConfig.authorize}
@@ -8,7 +8,7 @@ import type { JSXInternal } from "preact/src/jsx.js"
* on the default sign in page.
*/
export interface CredentialInput
extends Partial<JSXInternal.IntrinsicElements["input"]> {
extends Partial<JSX.IntrinsicElements["input"]> {
label?: string
}
@@ -32,12 +32,14 @@ export interface CredentialsConfig<
* @example
* ```ts
* //...
* async authorize(, request) {
* async authorize(credentials, request) {
* if(!isValidCredentials(credentials)) return null
* const response = await fetch(request)
* if(!response.ok) return null
* return await response.json() ?? null
* }
* //...
* ```
*/
authorize: (
/**

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Discord</b> integration.</span>
* <a href="https://discord.com/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/discord.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/discord
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/**
@@ -74,6 +84,54 @@ export interface DiscordProfile extends Record<string, any> {
image_url: string
}
/**
* Add Discord login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/discord
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Discord from "@auth/core/providers/discord"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Discord({ clientId: DISCORD_CLIENT_ID, clientSecret: DISCORD_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [Discord OAuth documentation](https://discord.com/developers/docs/topics/oauth2)
* - [Discord OAuth apps](https://discord.com/developers/applications)
*
* ### Notes
*
* By default, Auth.js assumes that the Discord provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Discord provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/discord.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Discord<P extends DiscordProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -1,50 +0,0 @@
/**
* @param {import("../core").Provider} options
* @example
*
* ```js
* // pages/api/auth/[...nextauth].js
* import Providers from `next-auth/providers`
* ...
* providers: [
* Providers.Dropbox({
* clientId: process.env.DROPBOX_CLIENT_ID,
* clientSecret: process.env.DROPBOX_CLIENT_SECRET
* })
* ]
* ...
*
* // pages/index
* import { signIn } from "next-auth/react"
* ...
* <button onClick={() => signIn("dropbox")}>
* Sign in
* </button>
* ...
* ```
* *Resources:*
* - [NextAuth.js Documentation](https://authjs.dev/reference/oauth-providers/dropbox)
* - [Dropbox Documentation](https://developers.dropbox.com/oauth-guide)
* - [Configuration](https://www.dropbox.com/developers/apps)
*/
/** @type {import(".").OAuthProvider} */
export default function Dropbox(options) {
return {
id: "dropbox",
name: "Dropbox",
type: "oauth",
authorization:
"https://www.dropbox.com/oauth2/authorize?token_access_type=offline&scope=account_info.read",
token: "https://api.dropboxapi.com/oauth2/token",
userinfo: "https://api.dropboxapi.com/2/users/get_current_account",
profile(profile) {
return {
id: profile.account_id,
name: profile.name.display_name,
email: profile.email,
image: profile.profile_photo_url,
}
},
options,
}
}

View File

@@ -0,0 +1,81 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Dropbox</b> integration.</span>
* <a href="https://dropbox.com/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/dropbox.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/dropbox
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/**
* Add Dropbox login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/dropbox
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Dropbox from "@auth/core/providers/dropbox"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Dropbox({ clientId: DROPBOX_CLIENT_ID, clientSecret: DROPBOX_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [Dropbox OAuth documentation](https://developers.dropbox.com/oauth-guide)
*
* ### Notes
*
* By default, Auth.js assumes that the Dropbox provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Dropbox provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/dropbox.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Dropbox(
options: OAuthUserConfig<Record<string, any>>
): OAuthConfig<Record<string, any>> {
return {
id: "dropbox",
name: "Dropbox",
type: "oauth",
authorization:
"https://www.dropbox.com/oauth2/authorize?token_access_type=offline&scope=account_info.read",
token: "https://api.dropboxapi.com/oauth2/token",
userinfo: "https://api.dropboxapi.com/2/users/get_current_account",
profile(profile) {
return {
id: profile.account_id,
name: profile.name.display_name,
email: profile.email,
image: profile.profile_photo_url,
}
},
options,
}
}

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>DuendeIdentityServer6</b> integration.</span>
* <a href="https://docs.duendesoftware.com/identityserver/v6">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/duende-identity-server6.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/duende-identity-server6
*/
import type { OAuthConfig, OAuthUserConfig } from "./oauth.js"
export interface DuendeISUser extends Record<string, any> {
@@ -7,6 +17,72 @@ export interface DuendeISUser extends Record<string, any> {
verified: boolean
}
/**
* Add DuendeIdentityServer6 login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/duende-identity-server6
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import DuendeIdentityServer6 from "@auth/core/providers/duende-identity-server6"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [DuendeIdentityServer6({ clientId: DIS6_CLIENT_ID, clientSecret: DIS6_CLIENT_SECRET, issuer: DIS6_ISSUER })],
* })
* ```
*
* ### Resources
*
* - [DuendeIdentityServer6 documentation](https://docs.duendesoftware.com/identityserver/v6)
*
* ### Notes
*
*
* ## Demo IdentityServer
*
* The configuration below is for the demo server at https://demo.duendesoftware.com/
*
* If you want to try it out, you can copy and paste the configuration below.
*
* You can sign in to the demo service with either <b>bob/bob</b> or <b>alice/alice</b>.
*
* ```js title=pages/api/auth/[...nextauth].js
* import DuendeIDS6Provider from "next-auth/providers/duende-identity-server6"
* providers: [
* DuendeIDS6Provider({
* clientId: "interactive.confidential",
* clientSecret: "secret",
* issuer: "https://demo.duendesoftware.com",
* })
* ]
* ```
* By default, Auth.js assumes that the DuendeIdentityServer6 provider is
* based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.
*
* :::tip
*
* The DuendeIdentityServer6 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/duende-identity-server6.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function DuendeIdentityServer6<P extends DuendeISUser>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -24,12 +24,12 @@ export interface SendVerificationRequestParams {
* - [SendGrid](https://sendgrid.com)
* - etc.
*
* @see [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
* [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
*/
export interface EmailConfig extends CommonProviderOptions {
type: "email"
// TODO: Make use of https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html
server: string | SMTPTransportOptions
server?: string | SMTPTransportOptions
/** @default `"Auth.js <no-reply@authjs.dev>"` */
from?: string
/**
@@ -72,7 +72,7 @@ export interface EmailConfig extends CommonProviderOptions {
* By default, we treat email addresses as all lower case,
* but you can override this function to change this behavior.
*
* [Documentation](https://authjs.dev/guides/providers/email#normalizing-the-e-mail-address) | [RFC 2821](https://tools.ietf.org/html/rfc2821) | [Email syntax](https://en.wikipedia.org/wiki/Email_address#Syntax)
* [Normalizing the email address](https://authjs.dev/reference/core/providers_email#normalizing-the-email-address) | [RFC 2821](https://tools.ietf.org/html/rfc2821) | [Email syntax](https://en.wikipedia.org/wiki/Email_address#Syntax)
*/
normalizeIdentifier?: (identifier: string) => string
}
@@ -81,7 +81,238 @@ export interface EmailConfig extends CommonProviderOptions {
// when started working on https://github.com/nextauthjs/next-auth/discussions/1465
export type EmailProviderType = "email"
/** TODO: */
/**
* ## Overview
* The Email provider uses email to send "magic links" that can be used to sign in, you will likely have seen these if you have used services like Slack before.
*
* Adding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).
*
* The Email provider can be used in conjunction with (or instead of) one or more OAuth providers.
* ### How it works
*
* On initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.
*
*
* If someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.
*
* :::tip
* The Email Provider can be used with both JSON Web Tokens and database sessions, but you **must** configure a database to use it. It is not possible to enable email sign in without using a database.
* :::
* ## Configuration
* 1. NextAuth.js does not include `nodemailer` as a dependency, so you'll need to install it yourself if you want to use the Email Provider. Run `npm install nodemailer` or `yarn add nodemailer`.
* 2. You will need an SMTP account; ideally for one of the [services known to work with `nodemailer`](https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/).
* 3. There are two ways to configure the SMTP server connection.
*
* You can either use a connection string or a `nodemailer` configuration object.
*
* 3.1 **Using a connection string**
*
* Create an `.env` file to the root of your project and add the connection string and email address.
*
* ```js title=".env" {1}
* EMAIL_SERVER=smtp://username:password@smtp.example.com:587
* EMAIL_FROM=noreply@example.com
* ```
*
* Now you can add the email provider like this:
*
* ```js {3} title="pages/api/auth/[...nextauth].js"
* import EmailProvider from "next-auth/providers/email";
* ...
* providers: [
* EmailProvider({
* server: process.env.EMAIL_SERVER,
* from: process.env.EMAIL_FROM
* }),
* ],
* ```
*
* 3.2 **Using a configuration object**
*
* In your `.env` file in the root of your project simply add the configuration object options individually:
*
* ```js title=".env"
* EMAIL_SERVER_USER=username
* EMAIL_SERVER_PASSWORD=password
* EMAIL_SERVER_HOST=smtp.example.com
* EMAIL_SERVER_PORT=587
* EMAIL_FROM=noreply@example.com
* ```
*
* Now you can add the provider settings to the NextAuth.js options object in the Email Provider.
*
* ```js title="pages/api/auth/[...nextauth].js"
* import EmailProvider from "next-auth/providers/email";
* ...
* providers: [
* EmailProvider({
* server: {
* host: process.env.EMAIL_SERVER_HOST,
* port: process.env.EMAIL_SERVER_PORT,
* auth: {
* user: process.env.EMAIL_SERVER_USER,
* pass: process.env.EMAIL_SERVER_PASSWORD
* }
* },
* from: process.env.EMAIL_FROM
* }),
* ],
* ```
*
* 4. Do not forget to setup one of the database [adapters](https://authjs.dev/reference/adapters) for storing the Email verification token.
*
* 5. You can now sign in with an email address at `/api/auth/signin`.
*
* A user account (i.e. an entry in the Users table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they use the link in the email.
*
* ## Customizing emails
*
* You can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `EmailProvider()`.
*
* e.g.
*
* ```js {3} title="pages/api/auth/[...nextauth].js"
* import EmailProvider from "next-auth/providers/email";
* ...
* providers: [
* EmailProvider({
* server: process.env.EMAIL_SERVER,
* from: process.env.EMAIL_FROM,
* sendVerificationRequest({
* identifier: email,
* url,
* provider: { server, from },
* }) {
* // your function
* },
* }),
* ]
* ```
*
* The following code shows the complete source for the built-in `sendVerificationRequest()` method:
*
* ```js
* import { createTransport } from "nodemailer"
*
* async function sendVerificationRequest(params) {
* const { identifier, url, provider, theme } = params
* const { host } = new URL(url)
* // NOTE: You are not required to use `nodemailer`, use whatever you want.
* const transport = createTransport(provider.server)
* const result = await transport.sendMail({
* to: identifier,
* from: provider.from,
* subject: `Sign in to ${host}`,
* text: text({ url, host }),
* html: html({ url, host, theme }),
* })
* const failed = result.rejected.concat(result.pending).filter(Boolean)
* if (failed.length) {
* throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`)
* }
* }
*
* function html(params: { url: string; host: string; theme: Theme }) {
* const { url, host, theme } = params
*
* const escapedHost = host.replace(/\./g, "&#8203;.")
*
* const brandColor = theme.brandColor || "#346df1"
* const color = {
* background: "#f9f9f9",
* text: "#444",
* mainBackground: "#fff",
* buttonBackground: brandColor,
* buttonBorder: brandColor,
* buttonText: theme.buttonText || "#fff",
* }
*
* return `
* <body style="background: ${color.background};">
* <table width="100%" border="0" cellspacing="20" cellpadding="0"
* style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;">
* <tr>
* <td align="center"
* style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
* Sign in to <strong>${escapedHost}</strong>
* </td>
* </tr>
* <tr>
* <td align="center" style="padding: 20px 0;">
* <table border="0" cellspacing="0" cellpadding="0">
* <tr>
* <td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><a href="${url}"
* target="_blank"
* style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">Sign
* in</a></td>
* </tr>
* </table>
* </td>
* </tr>
* <tr>
* <td align="center"
* style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
* If you did not request this email you can safely ignore it.
* </td>
* </tr>
* </table>
* </body>
* `
* }
*
* // Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)
* function text({ url, host }: { url: string; host: string }) {
* return `Sign in to ${host}\n${url}\n\n`
* }
* ```
*
* :::tip
* If you want to generate great looking email client compatible HTML with React, check out https://mjml.io
* :::
*
* ## Customizing the Verification Token
*
* By default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:
*
* ```js title="pages/api/auth/[...nextauth].js"
* providers: [
* EmailProvider({
* async generateVerificationToken() {
* return "ABC123"
* }
* })
* ],
* ```
*
* ## Normalizing the email address
*
* By default, Auth.js will normalize the email address. It treats values as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, eg. when looking up users by e-mail from databases.) and also removes any secondary email address that was passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `EmailProvider`. The following example shows the default behavior:
* ```ts
* EmailProvider({
* // ...
* normalizeIdentifier(identifier: string): string {
* // Get the first two elements only,
* // separated by `@` from user input.
* let [local, domain] = identifier.toLowerCase().trim().split("@")
* // The part before "@" can contain a ","
* // but we remove it on the domain part
* domain = domain.split(",")[0]
* return `${local}@${domain}`
*
* // You can also throw an error, which will redirect the user
* // to the sign-in page with error=EmailSignin in the URL
* // if (identifier.split("@").length > 2) {
* // throw new Error("Only one email allowed")
* // }
* },
* })
* ```
*
* :::warning
* Always make sure this returns a single e-mail address, even if multiple ones were passed in.
* :::
*/
export default function Email(config: EmailConfig): EmailConfig {
return {
id: "email",

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>EVEOnline</b> integration.</span>
* <a href="https://eveonline.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/eveonline.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/eveonline
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface EVEOnlineProfile extends Record<string, any> {
@@ -10,6 +20,73 @@ export interface EVEOnlineProfile extends Record<string, any> {
IntellectualProperty: string
}
/**
* Add EveOnline login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/eveonline
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import EveOnline from "@auth/core/providers/eveonline"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [EveOnline({ clientId: EVEONLINE_CLIENT_ID, clientSecret: EVEONLINE_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [EveOnline OAuth documentation](https://developers.eveonline.com/blog/article/sso-to-authenticated-calls)
*
* ### Notes
*
* :::tip
* When creating your application, make sure to select `Authentication Only` as the connection type.
* :::
*
* :::tip
* If using JWT for the session, you can add the `CharacterID` to the JWT and session. Example:
* ```js
* options: {
* jwt: {
* secret: process.env.JWT_SECRET,
* },
* callbacks: {
* session: async ({ session, token }) => {
* session.user.id = token.id;
* return session;
* }
* }
* }
* ```
* :::
* By default, Auth.js assumes that the EveOnline provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The EveOnline provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/eveonline.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function EVEOnline<P extends EVEOnlineProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -1,3 +1,13 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Facebook</b> integration.</span>
* <a href="https://facebook.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/facebook.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/facebook
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
interface FacebookPictureData {
@@ -12,6 +22,61 @@ export interface FacebookProfile extends Record<string, any> {
picture: FacebookPicture
}
/**
* Add Facebook login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/facebook
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Facebook from "@auth/core/providers/facebook"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Facebook({ clientId: FACEBOOK_CLIENT_ID, clientSecret: FACEBOOK_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [Facebook OAuth documentation](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/)
*
* ### Notes
*
* :::tip
* Production applications cannot use localhost URLs to sign in with Facebook. You need to use a dedicated development application in Facebook to use localhost callback URLs.
* :::
*
* :::tip
* Email address may not be returned for accounts created on mobile.
* :::
*
* By default, Auth.js assumes that the Facebook provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Facebook provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/facebook.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Facebook<P extends FacebookProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {

View File

@@ -1,25 +0,0 @@
/** @type {import(".").OAuthProvider} */
export default function FACEIT(options) {
return {
id: "faceit",
name: "FACEIT",
type: "oauth",
authorization: "https://accounts.faceit.com/accounts?redirect_popup=true",
headers: {
Authorization: `Basic ${Buffer.from(
`${options.clientId}:${options.clientSecret}`
).toString("base64")}`,
},
token: "https://api.faceit.com/auth/v1/oauth/token",
userinfo: "https://api.faceit.com/auth/v1/resources/userinfo",
profile(profile) {
return {
id: profile.guid,
name: profile.name,
email: profile.email,
image: profile.picture,
}
},
options,
}
}

Some files were not shown because too many files have changed in this diff Show More