mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
19 Commits
feat/vipps
...
@auth/svel
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c1a3b547e | ||
|
|
2534ae8801 | ||
|
|
14a120277b | ||
|
|
c49f484743 | ||
|
|
676b39d5b1 | ||
|
|
e27dbcab2f | ||
|
|
63805c7d75 | ||
|
|
5d1c35e8aa | ||
|
|
a77f557cbf | ||
|
|
657492d921 | ||
|
|
d1d63bddba | ||
|
|
99ac4899b5 | ||
|
|
2130765a57 | ||
|
|
4079274aaf | ||
|
|
3d7985fd6d | ||
|
|
331e0ce51e | ||
|
|
8af666a4cc | ||
|
|
fed0a67917 | ||
|
|
0332d86942 |
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -32,6 +32,7 @@ body:
|
||||
- "Azure Active Directory"
|
||||
- "Azure Active Directory B2C"
|
||||
- "Battlenet"
|
||||
- "Beyond Identity"
|
||||
- "Box"
|
||||
- "Bungie"
|
||||
- "Cognito"
|
||||
|
||||
4
.github/actions/issue-validator/repro.md
vendored
4
.github/actions/issue-validator/repro.md
vendored
@@ -14,9 +14,9 @@ Ensure the link is pointing to a codebase that is accessible (e.g. not a private
|
||||
|
||||
### **What happens if I don't provide a sufficient minimal reproduction?**
|
||||
|
||||
Issues with the `incomplete` label that receives no meaningful activity (e.g. new comments with a reproduction link) are automatically closed and locked after 30 days.
|
||||
Issues with the `incomplete` label that receives no meaningful activity (e.g. new comments with a reproduction link) are closed after 7 days.
|
||||
|
||||
If your issue has _not_ been resolved in that time and it has been closed/locked, please open a new issue with the required reproduction.
|
||||
If your issue has _not_ been resolved in that time and it has been closed/locked, please open a new issue with the required reproduction. (It's less likely that we check back on already closed issues.)
|
||||
|
||||
### **I did not open this issue, but it is relevant to me, what can I do to help?**
|
||||
|
||||
|
||||
@@ -17,9 +17,13 @@ AUTH0_ID=
|
||||
AUTH0_SECRET=
|
||||
AUTH0_ISSUER=
|
||||
|
||||
KEYCLOAK_ID=
|
||||
KEYCLOAK_SECRET=
|
||||
KEYCLOAK_ISSUER=
|
||||
# Beyond Identity Provider
|
||||
BEYOND_IDENTITY_CLIENT_ID=
|
||||
BEYOND_IDENTITY_CLIENT_SECRET=
|
||||
BEYOND_IDENTITY_ISSUER=
|
||||
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
|
||||
NOTION_ID=
|
||||
NOTION_SECRET=
|
||||
@@ -29,8 +33,15 @@ IDS4_ID=
|
||||
IDS4_SECRET=
|
||||
IDS4_ISSUER=
|
||||
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
KEYCLOAK_ID=
|
||||
KEYCLOAK_SECRET=
|
||||
KEYCLOAK_ISSUER=
|
||||
|
||||
LINE_ID=
|
||||
LINE_SECRET=
|
||||
|
||||
TRAKT_ID=
|
||||
TRAKT_SECRET=
|
||||
|
||||
TWITCH_ID=
|
||||
TWITCH_SECRET=
|
||||
@@ -38,11 +49,8 @@ TWITCH_SECRET=
|
||||
TWITTER_ID=
|
||||
TWITTER_SECRET=
|
||||
|
||||
LINE_ID=
|
||||
LINE_SECRET=
|
||||
|
||||
TRAKT_ID=
|
||||
TRAKT_SECRET=
|
||||
WIKIMEDIA_ID=
|
||||
WIKIMEDIA_SECRET=
|
||||
|
||||
# Example configuration for a Gmail account (will need SMTP enabled)
|
||||
EMAIL_SERVER=smtps://user@gmail.com:password@smtp.gmail.com:465
|
||||
@@ -55,12 +63,9 @@ EMAIL_FROM=user@gmail.com
|
||||
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
|
||||
DATABASE_URL=
|
||||
|
||||
WIKIMEDIA_ID=
|
||||
WIKIMEDIA_SECRET=
|
||||
|
||||
# Supabase Example Configuration
|
||||
# Supabase Example Configuration
|
||||
# NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
|
||||
# SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSJ9.vI9obAHOGyVVKa3pD--kJlyxp-Z2zV9UUMAhKpNLAcU
|
||||
# SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long
|
||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs
|
||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs
|
||||
|
||||
@@ -6,6 +6,7 @@ import Asgardeo from "@auth/core/providers/asgardeo"
|
||||
import Auth0 from "@auth/core/providers/auth0"
|
||||
import AzureAD from "@auth/core/providers/azure-ad"
|
||||
import AzureB2C from "@auth/core/providers/azure-ad-b2c"
|
||||
import BeyondIdentity from "@auth/core/providers/beyondidentity"
|
||||
import BoxyHQSAML from "@auth/core/providers/boxyhq-saml"
|
||||
// import Cognito from "@auth/core/providers/cognito"
|
||||
import Credentials from "@auth/core/providers/credentials"
|
||||
@@ -92,6 +93,7 @@ export const authConfig: AuthConfig = {
|
||||
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||
}),
|
||||
AzureB2C({ clientId: process.env.AZURE_B2C_ID, clientSecret: process.env.AZURE_B2C_SECRET, issuer: process.env.AZURE_B2C_ISSUER }),
|
||||
BeyondIdentity({ clientId: process.env.BEYOND_IDENTITY_CLIENT_ID, clientSecret: process.env.BEYOND_IDENTITY_CLIENT_SECRET, issuer: process.env.BEYOND_IDENTITY_ISSUER }),
|
||||
BoxyHQSAML({ issuer: "https://jackson-demo.boxyhq.com", clientId: "tenant=boxyhq.com&product=saml-demo.boxyhq.com", clientSecret: "dummy" }),
|
||||
// Cognito({ clientId: process.env.COGNITO_ID, clientSecret: process.env.COGNITO_SECRET, issuer: process.env.COGNITO_ISSUER }),
|
||||
Discord({ clientId: process.env.DISCORD_ID, clientSecret: process.env.DISCORD_SECRET }),
|
||||
|
||||
@@ -5,52 +5,40 @@ sidebar_position: 0
|
||||
|
||||
## About Auth.js
|
||||
|
||||
Auth.js is a complete open-source authentication solution for [Next.js](http://nextjs.org/) applications.
|
||||
Auth.js is a complete open-source authentication solution for web applications. Check out the live demos of Auth.js in action:
|
||||
|
||||
It is designed from the ground up to support Next.js and Serverless.
|
||||
- [Next.js](https://next-auth-example.vercel.app/)
|
||||
- [SvelteKit](https://sveltekit-auth-example.vercel.app/)
|
||||
- [SolidStart](https://auth-solid.vercel.app/)
|
||||
|
||||
Check our tutorials to see how easy it is to use Auth.js for authentication:
|
||||
Continue to our tutorials to see how to use Auth.js for authentication:
|
||||
|
||||
- [Setup with OAuth](/getting-started/oauth-tutorial)
|
||||
- [Setup with magic links](/getting-started/email-tutorial)
|
||||
- [Integrating with external auth](/getting-started/credentials-tutorial)
|
||||
|
||||
### Flexible and easy to use
|
||||
### Battery included
|
||||
|
||||
- Designed to work with any OAuth service, it supports OAuth 1.0, 1.0A, 2.0 and OpenID Connect
|
||||
- Built-in support for [many popular sign-in services](/reference/providers/oauth-builtin)
|
||||
- Supports [email / passwordless authentication](/getting-started/email-tutorial)
|
||||
- Supports stateless authentication with [any backend](/getting-started/credentials-tutorial) (Active Directory, LDAP, etc)
|
||||
- Supports both JSON Web Tokens and database sessions
|
||||
- Designed for Serverless but runs anywhere (AWS Lambda, Docker, Heroku, etc…)
|
||||
- Built in support for 60+ popular services (Google, Facebook, Auth0, Apple…)
|
||||
- Built-in email/password-less/magic link
|
||||
- Use with any OAuth 2 or OpenID Connect provider
|
||||
- Use with any username/password store
|
||||
|
||||
### Own your own data
|
||||
### Flexible
|
||||
- Runtime agnostic - run anywhere! Vercel Edge Functions, Node.js, Serverless, etc.
|
||||
- Use with any modern framework! Next.js, SolidStart, SvelteKit, etc.
|
||||
- [Bring Your Own Database](/getting-started/databases) - or none! MySQL, Postgres, MSSQL, MongoDB, etc. Choose database sessions or JWT.
|
||||
|
||||
Auth.js can be used with or without a database.
|
||||
|
||||
- An open-source solution that allows you to keep control of your data
|
||||
- Supports Bring Your Own Database (BYOD) and can be used with any database
|
||||
- Built-in support for [MySQL, MariaDB, Postgres, SQL Server, MongoDB and SQLite](/getting-started/databases)
|
||||
- Works great with databases from popular hosting providers
|
||||
- Can also be used _without a database_ (e.g. OAuth + JWT)
|
||||
|
||||
_Note: Email sign-in requires a database to be configured to store single-use verification tokens._
|
||||
_Note: Email sign-in requires a database to store single-use verification tokens._
|
||||
|
||||
### Secure by default
|
||||
|
||||
- Promotes the use of passwordless sign-in mechanisms
|
||||
- Designed to be secure by default and encourage best practices for safeguarding user data
|
||||
- Uses Cross-Site Request Forgery Tokens on POST routes (sign in, sign out)
|
||||
- Default cookie policy aims for the most restrictive policy appropriate for each cookie
|
||||
- When JSON Web Tokens are enabled, they are encrypted by default (JWE) with A256GCM
|
||||
- Auto-generates symmetric signing and encryption keys for developer convenience
|
||||
- Features tab/window syncing and keepalive messages to support short-lived sessions
|
||||
- Attempts to implement the latest guidance published by [Open Web Application Security Project](https://owasp.org/)
|
||||
|
||||
Advanced options allow you to define your own routines to handle controlling what accounts are allowed to sign in, for encoding and decoding JSON Web Tokens and to set custom cookie security policies and session properties, so you can control who can sign in and how often sessions have to be re-validated.
|
||||
- Signed, prefixed, server-only cookies
|
||||
- Built-in CSRF protection
|
||||
- Doesn't rely on client-side JavaScript
|
||||
- JWT with JWS / JWE / JWK.
|
||||
|
||||
## Credits
|
||||
|
||||
Auth.js is an open-source project that is only possible [thanks to contributors](/contributors).
|
||||
|
||||
If you would like to financially support the development of Auth.js, you can find more information on our [OpenCollective](https://opencollective.com/nextauth) page.
|
||||
To financially support the development of Auth.js, you can check our [OpenCollective](https://opencollective.com/nextauth) page. We appreciate your support 💚.
|
||||
|
||||
@@ -8,28 +8,43 @@ import gettingClientIdSecretImg from "./img/getting-started-oauth-clientid-secre
|
||||
import startAppAndSignInImg from "./img/getting-started-app-start.png"
|
||||
import githubAuthCredentials from "./img/getting-started-github-auth.png"
|
||||
import nextAuthUserLoggedIn from "./img/getting-started-nextauth-success.png"
|
||||
import Tabs from "@theme/Tabs"
|
||||
import TabItem from "@theme/TabItem"
|
||||
|
||||
We know, authentication is hard. It's a rabbit hole and it's easy to get lost on it. The goal of making Auth.js is that you can add authentication easily to your project with just a few lines of code.
|
||||
The goal of Auth.js is that you can add authentication easily to your project with just a few lines of code.
|
||||
|
||||
The easiest way is to setup Auth.js with an [OAuth](https://en.wikipedia.org/wiki/OAuth) provider. In this tutorial we'll be setting Auth.js in a **Next.js app** to be able to login with **Github**.
|
||||
The fastest way to set up Auth.js is with an [OAuth](/concepts/oauth) provider. In this tutorial, we'll be setting Auth.js in a web application to be able to log in with **GitHub**.
|
||||
|
||||
:::info
|
||||
Auth.js comes with a long list of [built-in providers](/reference/providers/oauth-builtin) (Google, Facebook, Twitter, etc...) you can also integrate it with your own OAuth service easily by [building a custom provider](/guides/providers/custom-provider). Auth.js can integrate as well with other frameworks like SvelteKit, SolidStart and Gatsby.
|
||||
Auth.js comes with a list of [built-in providers](/reference/providers/oauth-builtin) (Google, Facebook, Twitter, etc.). You can also integrate it with your OAuth service by [building a custom provider](/guides/providers/custom-provider).
|
||||
:::
|
||||
|
||||
## 1. Configuring Auth.js
|
||||
|
||||
To add Auth.js to your project:
|
||||
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
### Prerequisites
|
||||
|
||||
This tutorial assumes you have a Next.js application set up. If you don't, you can follow the [Next.js tutorial](https://nextjs.org/learn/basics/create-nextjs-app) to get started.
|
||||
|
||||
### Installing NextAuth.js
|
||||
|
||||
```bash npm2yarn
|
||||
npm install next-auth
|
||||
```
|
||||
|
||||
:::info
|
||||
We are working on a new `@auth/nextjs` package that will make it easier to set up Auth.js with Next.js. Stay tuned! For now, you can use the `next-auth` package.
|
||||
:::
|
||||
|
||||
### Creating the server config
|
||||
|
||||
To add Auth.js to a [**Next.js**](https://nextjs.org/) project, create the following [API route](https://nextjs.org/docs/api-routes/introduction):
|
||||
Create the following [API route](https://nextjs.org/docs/api-routes/dynamic-api-routes#catch-all-api-routes) file. This route contains the necessary configuration for NextAuth.js, as well as the dynamic route handler:
|
||||
|
||||
```
|
||||
pages/api/auth/[...nextauth].ts
|
||||
```
|
||||
|
||||
This route will contain the **dynamic route handler** for Auth.js which describes your global auth configuration:
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].js"
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
|
||||
@@ -43,16 +58,33 @@ export default NextAuth({
|
||||
})
|
||||
```
|
||||
|
||||
Behind the scenes this creates all the relevant OAuth API routes within `/api/auth/*` so that auth API requests to:
|
||||
:::info
|
||||
|
||||
- `/api/auth/callback`
|
||||
- `/api/auth/signIn`
|
||||
- `/api/auth/signOut`
|
||||
- etc...
|
||||
Behind the scenes, this creates all the relevant OAuth API routes within `/api/auth/*` so that auth API requests to:
|
||||
|
||||
can be handled by Auth.js. In this way, Auth.js stays in charge of handling the whole authentication request/response flow of your application for you.
|
||||
- [GET `/api/auth/signin`](https://authjs.dev/reference/rest-api#get--apiauthsignin)
|
||||
- [POST `/api/auth/signin/:provider`](https://authjs.dev/reference/rest-api#post--apiauthsigninprovider)
|
||||
- [GET/POST `/api/auth/callback/:provider`](https://authjs.dev/reference/rest-api#get--post--apiauthcallbackprovider)
|
||||
- [GET `/api/auth/signout`](https://authjs.dev/reference/rest-api#get--apiauthsignout)
|
||||
- [POST `/api/auth/signout`](https://authjs.dev/reference/rest-api#post--apiauthsignout)
|
||||
- [GET `/api/auth/session`](https://authjs.dev/reference/rest-api#get--apiauthsession)
|
||||
- [GET `/api/auth/csrf`](https://authjs.dev/reference/rest-api#get--apiauthcsrf)
|
||||
- [GET `/api/auth/providers`](https://authjs.dev/reference/rest-api#get--apiauthproviders)
|
||||
|
||||
You may notice there are some environment variables in the code example above. `GITHUB_ID` and `GITHUB_SECRET` are provided by the OAuth provider (in this case **Github**) see ["Configuring OAuth Provider"](/getting-started/oauth-tutorial#2-configuring-oauth-provider) section on how to get those.
|
||||
can be handled by NextAuth.js. In this way, NextAuth.js stays in charge of the whole application's authentication request/response flow.
|
||||
|
||||
NextAuth.js is fully customizable - [our guides section](/guides/overview) teaches you how to set it up to handle auth in different ways. All the possible configuration options are [listed here](/reference/configuration/auth-config).
|
||||
:::
|
||||
|
||||
### Adding environment variables
|
||||
|
||||
You may notice we are using environment variables in the code example above. We take the value of `GITHUB_ID` and `GITHUB_SECRET` from the GitHub Developer OAuth Portal. See [Configuring OAuth Provider](/getting-started/oauth-tutorial#2-configuring-oauth-provider) section on how to get those.
|
||||
|
||||
In your project root, create a `.env.local` file and add the `NEXTAUTH_SECRET` environment variable:
|
||||
|
||||
```title=".env.local"
|
||||
NEXTAUTH_SECRET="This is an example"
|
||||
```
|
||||
|
||||
`NEXTAUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
|
||||
|
||||
@@ -62,13 +94,9 @@ $ openssl rand -base64 32
|
||||
|
||||
or https://generate-secret.vercel.app/32 to generate a random value for it.
|
||||
|
||||
:::info
|
||||
Auth.js is extremely customizable, [our guides section](/guides/overview) will teach you how you can set it up to handle auth in different ways. All the possible configuration options are [listed here](/reference/configuration/auth-config).
|
||||
:::
|
||||
### Exposing the session via `SessionProvider`:
|
||||
|
||||
### Exposing the session via provider
|
||||
|
||||
To be able to use `useSession` first you'll need to expose the session context, [`<SessionProvider />`](/reference/react/#sessionprovider), at the top level of your application:
|
||||
NextAuth.js provides [`useSession()`](/reference/react/#usesession) - a [React Hooks](https://reactjs.org/docs/hooks-intro.html) to access the session data and status. To use it first you'll need to expose the session context - [`<SessionProvider />`](/reference/react/#sessionprovider) - at the top level of your application:
|
||||
|
||||
```ts title="pages/_app.tsx"
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
@@ -85,7 +113,7 @@ export default function App({
|
||||
}
|
||||
```
|
||||
|
||||
Instances of `useSession` (more on it in the next section) will then have access to the session data and status. The `<SessionProvider />` also takes care of keeping the session updated and synced between browser tabs and windows. 💪🏽
|
||||
Instances of `useSession` (more on it in the next section) will have access to the session data and status. The `<SessionProvider />` also keep the session updated and synced between browser tabs and windows. 💪🏽
|
||||
|
||||
:::tip
|
||||
Check our [client docs](/reference/react/) to learn all the available options for handling sessions on the browser.
|
||||
@@ -93,14 +121,14 @@ Check our [client docs](/reference/react/) to learn all the available options fo
|
||||
|
||||
### Consuming the session via hooks
|
||||
|
||||
Auth.js exposes a [`useSession()`](/reference/react/#usesession) React Hook so that you can easily check if someone is signed in:
|
||||
You can use the `useSession` hook from anywhere in your application (E.g. in a header component). Behind the scenes, the hook will connect to the `<SessionProvider />` to read the current user session. Learn more about React Context in the [React docs](https://reactjs.org/docs/context.html).
|
||||
|
||||
```ts title="pages/overview.tsx"
|
||||
import { useSession, signIn, signOut } from "next-auth/react"
|
||||
|
||||
export default function CamperVanPage() {
|
||||
const { data: session, status } = useSession()
|
||||
const userEmail = session?.user.email;
|
||||
const userEmail = session?.user.email
|
||||
|
||||
if (status === "loading") {
|
||||
return <p>Hang on there...</p>
|
||||
@@ -119,23 +147,22 @@ export default function CamperVanPage() {
|
||||
return (
|
||||
<>
|
||||
<p>Not signed in.</p>
|
||||
<button onClick={() => signIn()}>Sign in</button>
|
||||
<button onClick={() => signIn("github")}>Sign in</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
You can use the `useSession` hook from anywhere in your application (e.g. in a header component). Behind the scenes, the hook will connect to the `<SessionProvider />` to read the current user session.
|
||||
|
||||
### Protecting API Routes
|
||||
|
||||
Protecting your custom API Routes (.i.e not allowing a resource to be accessed in case the user is not logged in) is easy! You can use [`getSession()`](/reference/utilities/#getsession) to know whether a session exists or not:
|
||||
To protect your API Routes (blocking unauthorized access to resources), you can use [`getServerSession()`](/reference/nextjs#getserversession) to know whether a session exists or not:
|
||||
|
||||
```ts title="pages/api/movies/list.ts"
|
||||
import { getSession } from "next-auth/react"
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
|
||||
export default async function listMovies(req, res) {
|
||||
const session = await getSession({ req })
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
|
||||
if (session) {
|
||||
res.send({
|
||||
@@ -152,21 +179,33 @@ export default async function listMovies(req, res) {
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO: SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO: SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## 2. Configuring OAuth Provider
|
||||
|
||||
Ok, we have our Next.js app setup with NextAuth, however, if you run the app right now, it won't work as we haven't configured our OAuth provider (**Github**) yet.
|
||||
Ok, we have our app set up with NextAuth.js, however, if you run the app right now, it won't work as we haven't configured our OAuth provider (**GitHub**) yet.
|
||||
|
||||
:::info
|
||||
When using OAuth you're asking for a third-party service (in this case Github, although it could be Google, Twitter, etc...) to handle user authentication for your app.
|
||||
When using OAuth you're asking for a third-party service (in this case GitHub, although it could be Google, Twitter, etc...) to handle user authentication for your app.
|
||||
:::
|
||||
|
||||
We need to register our new Next.js app in Github, so that when Auth.js forwards the authorization requests to it, Github can recognize your application and prompt the user to sign in.
|
||||
We need to register our new app in GitHub, so that when NextAuth.js forwards the authorization requests to it, GitHub can recognize your application and prompt the user to sign in.
|
||||
|
||||
<img src={creatingOauthAppImg} />
|
||||
|
||||
Log in into **Github**, go to `Settings / Developers / OAuth Apps` and click on "New OAuth App".
|
||||
Log in to **GitHub**, go to [`Settings / Developers / OAuth Apps`](https://github.com/settings/developers) and click "New OAuth App"
|
||||
|
||||
Next you'll be presented with a screen to add details about your new application. Fill in the required fields, but pay extra attention to the **Authorization Callback URL** one:
|
||||
Next, you'll be presented with a screen to add details about your new application. Fill in the required fields, but pay extra attention to the **Authorization Callback URL** one:
|
||||
|
||||
<img src={addingCallbackUrlImg} />
|
||||
|
||||
@@ -176,37 +215,54 @@ The callback URL we insert should have the following pattern:
|
||||
[origin]/api/auth/callback/[provider]
|
||||
```
|
||||
|
||||
In this case, given we want to try our authentication working locally on our machine and we're using **Github** as our OAuth provider, it'll be:
|
||||
In this case, given we want to try our authentication working locally on our machine and we're using **GitHub** as our OAuth provider, it'll be:
|
||||
|
||||
<Tabs groupId="frameworks">
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
```
|
||||
http://localhost:3000/api/auth/callback/github
|
||||
```
|
||||
|
||||
:::info
|
||||
Auth.js will already magically create this API endpoint for you when we start the application later. Note that because we're using Next.js, locally it starts our server on the port `3000`, hence the origin is `http://localhost:3000`.
|
||||
NextAuth.js will already create this API endpoint for you when we start the application later. Note that because we're using Next.js, locally it starts our server on port `3000` by default. Hence, the origin is `http://localhost:3000`.
|
||||
:::
|
||||
|
||||
Next you'll be presented with the following screen which presents all the configuration for your new OAuth app. For now, we need two things from it: the **Client ID** and **Client Secret** for our new OAuth app:
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
|
||||
```
|
||||
http://localhost:5173/auth/callback/github
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
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**:
|
||||
|
||||
<img src={gettingClientIdSecretImg} />
|
||||
|
||||
The Client ID is always there, a public identifier of your OAuth application within Github. Click on the **Generate a new client Secret** button and should be presented with a new string (which is just a randomized string).
|
||||
The Client ID is always there, a public identifier of your OAuth application within GitHub. Click on the **Generate a new client Secret** button and should be presented with a new string (which is just a randomized string).
|
||||
|
||||
:::warning
|
||||
🔥 Keep both your Client ID and Client Secret secure and never expose them to the public or shared with people outside your organization. With them, a malicious actor could hijack your application and cause you and your user serious problems!
|
||||
Keep both your Client ID and Client Secret secure and never expose them to the public or share them with people outside your organization. With them, a malicious actor could hijack your application and cause you and your user serious problems!
|
||||
:::
|
||||
|
||||
Now let's copy both the Client ID and Client Secret and paste them in an environment file in the root of your project like so:
|
||||
|
||||
```title=".env.local"
|
||||
GITHUB_ID=12345
|
||||
GITHUB_SECRET=67890
|
||||
```
|
||||
|
||||
Cool! We have finished the configuring our OAuth provider, now let's wire all together so we can finally see authentication working in our app!
|
||||
Cool! We have finished configuring our OAuth provider, now let's wire all together so we can finally see authentication working in our app!
|
||||
|
||||
:::info
|
||||
As noted previously, Auth.js has built-in support for multiple OAuth providers, <a href="">here the full list</a>. You can also easily build your own in case the provider you need is not on the list.
|
||||
As noted previously, NextAuth.js has built-in support for multiple OAuth providers, <a href="">here is the full list</a>. You can also easily build your own in case the provider you need is not on the list.
|
||||
|
||||
Note that, for each provider, the configuration process will be similar to what we just did:
|
||||
|
||||
@@ -218,9 +274,21 @@ Note that, for each provider, the configuration process will be similar to what
|
||||
|
||||
## 3. Wiring all together
|
||||
|
||||
Finally, we just need to reference our **Client ID** and **Client Secret** we just generated in the previous in our Auth.js config. In this way the library will be able to use them when forwarding users to Github, and Github will be able to recognize the request as generated from our application:
|
||||
Finally, we just need to reference our **Client ID** and **Client Secret** we just generated in the previous in our Auth.js config. In this way, the library will be able to use them when forwarding users to GitHub, and GitHub will be able to recognize the request as generated from our application.
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].js"
|
||||
Now let's copy both the Client ID and Client Secret and paste them into an environment file in the root of your project like so:
|
||||
|
||||
```title=".env.local"
|
||||
GITHUB_ID=12345
|
||||
GITHUB_SECRET=67890
|
||||
```
|
||||
|
||||
Here is our server configuration file again:
|
||||
|
||||
<Tabs groupId="frameworks">
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
|
||||
@@ -240,52 +308,52 @@ Great! We're now ready to run our application locally. Start the Next.js app by
|
||||
$ npm run next dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You should see the following page:
|
||||
|
||||
<img src={startAppAndSignInImg} />
|
||||
|
||||
Click on "Sign in" and then on "Sign in with Github": Auth.js will redirect you to Github, and Github will recognize our app [that we just registered](#2-configuring-oauth-provider) and ask the user (in this case you) to enter its credentials to proceed:
|
||||
Click on "Sign in" and then on "Sign in with GitHub": Auth.js will redirect you to GitHub, and GitHub will recognize our app [that we just registered](#2-configuring-oauth-provider) and ask the user (in this case you) to enter its credentials to proceed:
|
||||
|
||||
<img src={githubAuthCredentials} />
|
||||
|
||||
Once inserted and correct, Github will redirect the user to our app and Auth.js will take care of any further calls with Github to get access to the user profile and start a user sessions safely in the background:
|
||||
Once inserted and correct, GitHub will redirect the user to our app and NextAuth.js will take care of any further calls with GitHub to get access to the user profile and start a user session safely in the background:
|
||||
|
||||
<img src={nextAuthUserLoggedIn} />
|
||||
|
||||
Great! We have completed the whole E2E authentication flow setup so that users can login in our application through Github!
|
||||
:::
|
||||
|
||||
:::info
|
||||
You can create your own Sign In page instead of using the default one from Auth.js. You can learn how to do so in our [dedicated guide for it](/guides/basics/pages).
|
||||
:::
|
||||
|
||||
Great! We have completed the whole E2E authentication flow setup so that users can log in to our application through GitHub!
|
||||
|
||||
## 4. Deploying to production
|
||||
|
||||
### Configuring different environments
|
||||
|
||||
It's normal to test your application under different environments. Usually you'll have a development environment (when you run the application locally in your machine), a staging environment (for teams members to try the application) and a production environment.
|
||||
It's normal to test your application in different environments. Usually, you'll have a development environment (when you run the application locally on your machine), a staging environment (for team members to try the application), and a production environment.
|
||||
|
||||
For each environment, you're going to need to create an OAuth application in your provider respectively, as [we did previously](#2-configuring-oauth-provider), and point the **callback URL** to it.
|
||||
For each environment, you need to create an OAuth application in your provider respectively, as [we did previously](#2-configuring-oauth-provider), and point the **callback URL** to it.
|
||||
|
||||
For instance in the previous section, we pointed the callback URL to:
|
||||
For instance, in the previous section, we pointed the callback URL to `http://localhost:3000/api/auth/callback/github` as we wanted to test our application in the development environment.
|
||||
|
||||
```
|
||||
http://localhost:3000/api/auth/callback/github
|
||||
```
|
||||
If we were to deploy our app to production, we would need to create a new **OAuth App** in GitHub (calling it something like "Van life – prod") and point the **callback URL** to our production domain: `https://example.com/api/auth/callback/github`
|
||||
|
||||
as we wanted to test our application in the development environment.
|
||||
|
||||
If we were to deploy our app to production, we would need to create again a new **OAuth App** in Github (calling it something like "Van life – prod") and point the **callback URL** to our production domain:
|
||||
|
||||
```
|
||||
https://example.com/api/auth/callback/github
|
||||
```
|
||||
|
||||
Finally, we would need just to point the environment variables we set ( `GITHUB_ID` and `GITHUB_SECRET` ) to the credentials of the OAuth app we want our application to run against.
|
||||
Finally, we would need to point the environment variables we set ( `GITHUB_ID` and `GITHUB_SECRET` ) to the credentials of the OAuth app we want our application to run with.
|
||||
|
||||
### Setting up `NEXTAUTH_URL`
|
||||
|
||||
:::tip
|
||||
Skip this section if you are deploying to Vercel.
|
||||
:::
|
||||
|
||||
When deploying your site, **you need to set** the `NEXTAUTH_URL` environment variable to the canonical URL of your website:
|
||||
|
||||
```
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Role-based authentication
|
||||
title: Role-based access control
|
||||
---
|
||||
|
||||
There are two ways to add role-based authentication (RBAC) to your application, based on the [session strategy](/concepts/session-strategies) you choose. Let's see an example for each of these.
|
||||
There are two ways to add role-based access control (RBAC) to your application, based on the [session strategy](/concepts/session-strategies) you choose. Let's see an example for each of these.
|
||||
|
||||
## Getting the role
|
||||
|
||||
@@ -150,4 +150,4 @@ When using Next.js and JWT, you can alternatively also use [Middleware](https://
|
||||
- [Next.js: Middleware](https://next-auth.js.org/configuration/nextjs#wrap-middleware)
|
||||
- [Adapters: User model](/reference/adapters/models#user)
|
||||
- [Adapters: Prisma adapter](/reference/adapters/prisma)
|
||||
- [TypeScript](/getting-started/typescript)
|
||||
- [TypeScript](/getting-started/typescript)
|
||||
@@ -45,7 +45,7 @@ You need to use at least Prisma 2.26.0. Create a schema file in `prisma/schema.p
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
shadowDatabaseUrl = env("SHADOW_DATABASE_URL") // Only needed when using a cloud provider that doesn't support the creation of new databases, like Heroku. Learn more: https://pris.ly/migrate-shadow
|
||||
shadowDatabaseUrl = env("SHADOW_DATABASE_URL") // Only needed when using a cloud provider that doesn't support the creation of new databases, like Heroku. Learn more: https://pris.ly/d/migrate-shadow
|
||||
}
|
||||
|
||||
generator client {
|
||||
|
||||
@@ -54,18 +54,6 @@ module.exports = {
|
||||
{ type: "autogenerated", dirName: "reference/06-adapters" },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "OAuth Providers",
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "reference/05-oauth-providers",
|
||||
// See: https://github.com/facebook/docusaurus/issues/5689
|
||||
// exclude: ["index"],
|
||||
},
|
||||
],
|
||||
},
|
||||
"reference/utilities/client",
|
||||
"reference/warnings",
|
||||
],
|
||||
|
||||
4
docs/static/img/providers/beyondidentity-dark.svg
vendored
Normal file
4
docs/static/img/providers/beyondidentity-dark.svg
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" role="img">
|
||||
<!-- <path d="M15.968 0 7.684 16.57 3.763 8.726H.949L7.684 21.47 18.783 0h-2.815ZM6.767 27.162v2.878h2.498v-7.747l-2.498 4.869Z" fill="#5077C5"/> -->
|
||||
<path d="M 22.102,0 13.818,16.57 9.897,8.726 H 7.083 L 13.818,21.47 24.917,0 Z m -9.201,27.162 v 2.878 h 2.498 v -7.747 z" fill="#5077c5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 408 B |
4
docs/static/img/providers/beyondidentity.svg
vendored
Normal file
4
docs/static/img/providers/beyondidentity.svg
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" role="img">
|
||||
<!-- <path d="M15.968 0 7.684 16.57 3.763 8.726H.949L7.684 21.47 18.783 0h-2.815ZM6.767 27.162v2.878h2.498v-7.747l-2.498 4.869Z" fill="#5077C5"/> -->
|
||||
<path d="M 22.102,0 13.818,16.57 9.897,8.726 H 7.083 L 13.818,21.47 24.917,0 Z m -9.201,27.162 v 2.878 h 2.498 v -7.747 z" fill="#5077c5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 408 B |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/dgraph-adapter",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"description": "Dgraph adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -50,4 +50,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@next-auth/dynamodb-adapter",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "AWS DynamoDB adapter for next-auth.",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/firebase-adapter",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"description": "Firebase adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* ## Installation
|
||||
*
|
||||
* ```bash npm2yarn2pnpm
|
||||
* npm install next-auth @next-auth/firebase-admin-adapter firebase-admin
|
||||
* npm install next-auth @next-auth/firebase-adapter firebase-admin
|
||||
* ```
|
||||
*
|
||||
* ## References
|
||||
@@ -22,20 +22,22 @@
|
||||
* @module @next-auth/firebase-adapter
|
||||
*/
|
||||
|
||||
import { type AppOptions } from "firebase-admin"
|
||||
import { Firestore } from "firebase-admin/firestore"
|
||||
import { type AppOptions, getApps, initializeApp } from "firebase-admin/app"
|
||||
|
||||
import type { Adapter, AdapterUser } from "next-auth/adapters"
|
||||
import {
|
||||
collestionsFactory,
|
||||
deleteDocs,
|
||||
initFirestore,
|
||||
getDoc,
|
||||
getOneDoc,
|
||||
mapFieldsFactory,
|
||||
} from "./utils"
|
||||
Firestore,
|
||||
getFirestore,
|
||||
initializeFirestore,
|
||||
Timestamp,
|
||||
} from "firebase-admin/firestore"
|
||||
|
||||
export { initFirestore } from "./utils"
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
VerificationToken,
|
||||
} from "next-auth/adapters"
|
||||
|
||||
/** Configure the Firebase Adapter. */
|
||||
export interface FirebaseAdapterConfig extends AppOptions {
|
||||
@@ -300,3 +302,156 @@ export function FirestoreAdapter(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// for consistency, store all fields as snake_case in the database
|
||||
const MAP_TO_FIRESTORE: Record<string, string | undefined> = {
|
||||
userId: "user_id",
|
||||
sessionToken: "session_token",
|
||||
providerAccountId: "provider_account_id",
|
||||
emailVerified: "email_verified",
|
||||
}
|
||||
const MAP_FROM_FIRESTORE: Record<string, string | undefined> = {}
|
||||
|
||||
for (const key in MAP_TO_FIRESTORE) {
|
||||
MAP_FROM_FIRESTORE[MAP_TO_FIRESTORE[key]!] = key
|
||||
}
|
||||
|
||||
const identity = <T>(x: T) => x
|
||||
|
||||
/** @internal */
|
||||
export function mapFieldsFactory(preferSnakeCase?: boolean) {
|
||||
if (preferSnakeCase) {
|
||||
return {
|
||||
toDb: (field: string) => MAP_TO_FIRESTORE[field] ?? field,
|
||||
fromDb: (field: string) => MAP_FROM_FIRESTORE[field] ?? field,
|
||||
}
|
||||
}
|
||||
return { toDb: identity, fromDb: identity }
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function getConverter<Document extends Record<string, any>>(options: {
|
||||
excludeId?: boolean
|
||||
preferSnakeCase?: boolean
|
||||
}): FirebaseFirestore.FirestoreDataConverter<Document> {
|
||||
const mapper = mapFieldsFactory(options?.preferSnakeCase ?? false)
|
||||
|
||||
return {
|
||||
toFirestore(object) {
|
||||
const document: Record<string, unknown> = {}
|
||||
|
||||
for (const key in object) {
|
||||
if (key === "id") continue
|
||||
const value = object[key]
|
||||
if (value !== undefined) {
|
||||
document[mapper.toDb(key)] = value
|
||||
} else {
|
||||
console.warn(`FirebaseAdapter: value for key "${key}" is undefined`)
|
||||
}
|
||||
}
|
||||
|
||||
return document
|
||||
},
|
||||
|
||||
fromFirestore(
|
||||
snapshot: FirebaseFirestore.QueryDocumentSnapshot<Document>
|
||||
): Document {
|
||||
const document = snapshot.data()! // we can guarantee it exists
|
||||
|
||||
const object: Record<string, unknown> = {}
|
||||
|
||||
if (!options?.excludeId) {
|
||||
object.id = snapshot.id
|
||||
}
|
||||
|
||||
for (const key in document) {
|
||||
let value: any = document[key]
|
||||
if (value instanceof Timestamp) value = value.toDate()
|
||||
|
||||
object[mapper.fromDb(key)] = value
|
||||
}
|
||||
|
||||
return object as Document
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function getOneDoc<T>(
|
||||
querySnapshot: FirebaseFirestore.Query<T>
|
||||
): Promise<T | null> {
|
||||
const querySnap = await querySnapshot.limit(1).get()
|
||||
return querySnap.docs[0]?.data() ?? null
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
async function deleteDocs<T>(
|
||||
querySnapshot: FirebaseFirestore.Query<T>
|
||||
): Promise<void> {
|
||||
const querySnap = await querySnapshot.get()
|
||||
for (const doc of querySnap.docs) {
|
||||
await doc.ref.delete()
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function getDoc<T>(
|
||||
docRef: FirebaseFirestore.DocumentReference<T>
|
||||
): Promise<T | null> {
|
||||
const docSnap = await docRef.get()
|
||||
return docSnap.data() ?? null
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function collestionsFactory(
|
||||
db: FirebaseFirestore.Firestore,
|
||||
preferSnakeCase = false
|
||||
) {
|
||||
return {
|
||||
users: db
|
||||
.collection("users")
|
||||
.withConverter(getConverter<AdapterUser>({ preferSnakeCase })),
|
||||
sessions: db
|
||||
.collection("sessions")
|
||||
.withConverter(getConverter<AdapterSession>({ preferSnakeCase })),
|
||||
accounts: db
|
||||
.collection("accounts")
|
||||
.withConverter(getConverter<AdapterAccount>({ preferSnakeCase })),
|
||||
verification_tokens: db
|
||||
.collection(
|
||||
preferSnakeCase ? "verification_tokens" : "verificationTokens"
|
||||
)
|
||||
.withConverter(
|
||||
getConverter<VerificationToken>({ preferSnakeCase, excludeId: true })
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that helps making sure that there is no duplicate app initialization issues in serverless environments.
|
||||
* If no parameter is passed, it will use the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to initialize a Firestore instance.
|
||||
*
|
||||
* @example
|
||||
* ```ts title="lib/firestore.ts"
|
||||
* import { initFirestore } from "@next-auth/firebase-adapter"
|
||||
* import { cert } from "firebase-admin/app"
|
||||
*
|
||||
* export const firestore = initFirestore({
|
||||
* credential: cert({
|
||||
* projectId: process.env.FIREBASE_PROJECT_ID,
|
||||
* clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
|
||||
* privateKey: process.env.FIREBASE_PRIVATE_KEY,
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function initFirestore(
|
||||
options: AppOptions & { name?: FirebaseAdapterConfig["name"] } = {}
|
||||
) {
|
||||
const apps = getApps()
|
||||
const app = options.name ? apps.find((a) => a.name === options.name) : apps[0]
|
||||
|
||||
if (app) return getFirestore(app)
|
||||
|
||||
return initializeFirestore(initializeApp(options, options.name))
|
||||
}
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
import { AppOptions, getApps, initializeApp } from "firebase-admin/app"
|
||||
|
||||
import {
|
||||
getFirestore,
|
||||
initializeFirestore,
|
||||
Timestamp,
|
||||
} from "firebase-admin/firestore"
|
||||
|
||||
import type {
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
VerificationToken,
|
||||
} from "next-auth/adapters"
|
||||
import { FirebaseAdapterConfig } from "."
|
||||
|
||||
// for consistency, store all fields as snake_case in the database
|
||||
const MAP_TO_FIRESTORE: Record<string, string | undefined> = {
|
||||
userId: "user_id",
|
||||
sessionToken: "session_token",
|
||||
providerAccountId: "provider_account_id",
|
||||
emailVerified: "email_verified",
|
||||
}
|
||||
const MAP_FROM_FIRESTORE: Record<string, string | undefined> = {}
|
||||
|
||||
for (const key in MAP_TO_FIRESTORE) {
|
||||
MAP_FROM_FIRESTORE[MAP_TO_FIRESTORE[key]!] = key
|
||||
}
|
||||
|
||||
const identity = <T>(x: T) => x
|
||||
|
||||
/** @internal */
|
||||
export function mapFieldsFactory(preferSnakeCase?: boolean) {
|
||||
if (preferSnakeCase) {
|
||||
return {
|
||||
toDb: (field: string) => MAP_TO_FIRESTORE[field] ?? field,
|
||||
fromDb: (field: string) => MAP_FROM_FIRESTORE[field] ?? field,
|
||||
}
|
||||
}
|
||||
return { toDb: identity, fromDb: identity }
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getConverter<Document extends Record<string, any>>(options: {
|
||||
excludeId?: boolean
|
||||
preferSnakeCase?: boolean
|
||||
}): FirebaseFirestore.FirestoreDataConverter<Document> {
|
||||
const mapper = mapFieldsFactory(options?.preferSnakeCase ?? false)
|
||||
|
||||
return {
|
||||
toFirestore(object) {
|
||||
const document: Record<string, unknown> = {}
|
||||
|
||||
for (const key in object) {
|
||||
if (key === "id") continue
|
||||
const value = object[key]
|
||||
if (value !== undefined) {
|
||||
document[mapper.toDb(key)] = value
|
||||
} else {
|
||||
console.warn(`FirebaseAdapter: value for key "${key}" is undefined`)
|
||||
}
|
||||
}
|
||||
|
||||
return document
|
||||
},
|
||||
|
||||
fromFirestore(
|
||||
snapshot: FirebaseFirestore.QueryDocumentSnapshot<Document>
|
||||
): Document {
|
||||
const document = snapshot.data()! // we can guarantee it exists
|
||||
|
||||
const object: Record<string, unknown> = {}
|
||||
|
||||
if (!options?.excludeId) {
|
||||
object.id = snapshot.id
|
||||
}
|
||||
|
||||
for (const key in document) {
|
||||
let value: any = document[key]
|
||||
if (value instanceof Timestamp) value = value.toDate()
|
||||
|
||||
object[mapper.fromDb(key)] = value
|
||||
}
|
||||
|
||||
return object as Document
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function getOneDoc<T>(
|
||||
querySnapshot: FirebaseFirestore.Query<T>
|
||||
): Promise<T | null> {
|
||||
const querySnap = await querySnapshot.limit(1).get()
|
||||
return querySnap.docs[0]?.data() ?? null
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function deleteDocs<T>(
|
||||
querySnapshot: FirebaseFirestore.Query<T>
|
||||
): Promise<void> {
|
||||
const querySnap = await querySnapshot.get()
|
||||
for (const doc of querySnap.docs) {
|
||||
await doc.ref.delete()
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function getDoc<T>(
|
||||
docRef: FirebaseFirestore.DocumentReference<T>
|
||||
): Promise<T | null> {
|
||||
const docSnap = await docRef.get()
|
||||
return docSnap.data() ?? null
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function collestionsFactory(
|
||||
db: FirebaseFirestore.Firestore,
|
||||
preferSnakeCase = false
|
||||
) {
|
||||
return {
|
||||
users: db
|
||||
.collection("users")
|
||||
.withConverter(getConverter<AdapterUser>({ preferSnakeCase })),
|
||||
sessions: db
|
||||
.collection("sessions")
|
||||
.withConverter(getConverter<AdapterSession>({ preferSnakeCase })),
|
||||
accounts: db
|
||||
.collection("accounts")
|
||||
.withConverter(getConverter<AdapterAccount>({ preferSnakeCase })),
|
||||
verification_tokens: db
|
||||
.collection(
|
||||
preferSnakeCase ? "verification_tokens" : "verificationTokens"
|
||||
)
|
||||
.withConverter(
|
||||
getConverter<VerificationToken>({ preferSnakeCase, excludeId: true })
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that helps making sure that there is no duplicate app initialization issues in serverless environments.
|
||||
* If no parameter is passed, it will use the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to initialize a Firestore instance.
|
||||
*
|
||||
* @example
|
||||
* ```ts title="lib/firestore.ts"
|
||||
* import { initFirestore } from "@next-auth/firebase-adapter"
|
||||
* import { cert } from "firebase-admin/app"
|
||||
*
|
||||
* export const firestore = initFirestore({
|
||||
* credential: cert({
|
||||
* projectId: process.env.FIREBASE_PROJECT_ID,
|
||||
* clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
|
||||
* privateKey: process.env.FIREBASE_PRIVATE_KEY,
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function initFirestore(
|
||||
options: AppOptions & { name?: FirebaseAdapterConfig["name"] } = {}
|
||||
) {
|
||||
const apps = getApps()
|
||||
const app = options.name ? apps.find((a) => a.name === options.name) : apps[0]
|
||||
|
||||
if (app) return getFirestore(app)
|
||||
|
||||
return initializeFirestore(initializeApp(options, options.name))
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
getDoc,
|
||||
getOneDoc,
|
||||
mapFieldsFactory,
|
||||
} from "../src/utils"
|
||||
} from "../src"
|
||||
|
||||
describe.each([
|
||||
{ namingStrategy: "snake_case" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/pouchdb-adapter",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.6",
|
||||
"description": "PouchDB adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -51,4 +51,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/upstash-redis-adapter",
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.4",
|
||||
"description": "Upstash adapter for next-auth. It uses Upstash's connectionless (HTTP based) Redis client.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -49,4 +49,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/core",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.1",
|
||||
"description": "Authentication for the Web.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
@@ -61,7 +61,7 @@
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@panva/hkdf": "^1.0.2",
|
||||
"@panva/hkdf": "^1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"jose": "^4.11.1",
|
||||
"oauth4webapi": "^2.0.6",
|
||||
|
||||
@@ -101,7 +101,7 @@ export async function init({
|
||||
// Asserted in assert.ts
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
secret: authOptions.secret!,
|
||||
maxAge, // same as session maxAge,
|
||||
maxAge: authOptions.session?.maxAge ?? maxAge, // default to same as `session.maxAge`
|
||||
encode: jwt.encode,
|
||||
decode: jwt.decode,
|
||||
...authOptions.jwt,
|
||||
|
||||
@@ -96,9 +96,11 @@ function normalizeEndpoint(
|
||||
// NOTE: This need to be checked when constructing the URL
|
||||
// for the authorization, token and userinfo endpoints.
|
||||
const url = new URL(e?.url ?? "https://authjs.dev")
|
||||
for (const k in e?.params) {
|
||||
if (e?.params && k === "claims") e.params[k] = JSON.stringify(e.params[k])
|
||||
url.searchParams.set(k, e?.params[k])
|
||||
if (e?.params != null) {
|
||||
for (let [key, value] of Object.entries(e.params)) {
|
||||
if (key === "claims") value = JSON.stringify(value)
|
||||
url.searchParams.set(key, String(value))
|
||||
}
|
||||
}
|
||||
return { url, request: e?.request, conform: e?.conform }
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ export async function session(
|
||||
const newToken = await jwt.encode({
|
||||
...jwt,
|
||||
token,
|
||||
maxAge: options.session.maxAge,
|
||||
})
|
||||
|
||||
// Set cookie, to also update expiry date on cookie
|
||||
@@ -73,7 +72,7 @@ export async function session(
|
||||
await events.session?.({ session: newSession, token })
|
||||
} else {
|
||||
response.cookies?.push(...sessionStore.clean())
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(new JWTSessionError(e as Error))
|
||||
// If the JWT is not verifiable remove the broken session cookie(s).
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
/**
|
||||
* <div style={{backgroundColor: "#0072c6", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Azure AD B2C</b> integration.</span>
|
||||
* <a href="https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/azure-dark.svg" height="48" width="48"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* ---
|
||||
* @module providers/azure-ad-b2c
|
||||
*/
|
||||
|
||||
export interface AzureB2CProfile extends Record<string, any> {
|
||||
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
|
||||
|
||||
/** @see [Claims](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tokens-overview#claims) */
|
||||
export interface AzureADB2CProfile {
|
||||
exp: number
|
||||
nbf: number
|
||||
ver: string
|
||||
@@ -17,12 +30,85 @@ export interface AzureB2CProfile extends Record<string, any> {
|
||||
tfp: string
|
||||
}
|
||||
|
||||
export default function AzureADB2C<P extends AzureB2CProfile>(
|
||||
options: OAuthUserConfig<P> & {
|
||||
/**
|
||||
* Add Azure AD B2C login to your page.
|
||||
*
|
||||
*
|
||||
* ## Configuration
|
||||
*
|
||||
* ### Basic
|
||||
*
|
||||
* Basic configuration sets up Azure AD B2C to return an ID Token. This should be done as a prerequisite prior to running through the Advanced configuration.
|
||||
*
|
||||
* 1. [Azure AD B2C Tenant](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant)
|
||||
* 2. [App Registration](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications)
|
||||
* 3. [User Flow](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows)
|
||||
*
|
||||
* For the step "User attributes and token claims" you might want to set the following:
|
||||
*
|
||||
* - Collect attribute:
|
||||
* - Email Address
|
||||
* - Display Name
|
||||
* - Given Name
|
||||
* - Surname
|
||||
* - Return claim:
|
||||
* - Email Addresses
|
||||
* - Display Name
|
||||
* - Given Name
|
||||
* - Surname
|
||||
* - Identity Provider
|
||||
* - Identity Provider Access Token
|
||||
* - User's Object ID
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```ts
|
||||
* import { Auth } from "@auth/core"
|
||||
* import AzureADB2C from "@auth/core/providers/azure-ad-b2c"
|
||||
*
|
||||
* const request = new Request("https://example.com")
|
||||
* const response = await AuthHandler(request, {
|
||||
* // optionally, you can pass `tenantId` and `primaryUserFlow` instead of `issuer`
|
||||
* providers: [AzureADB2C({ clientId: "", clientSecret: "", issuer: "" })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* ## Resources
|
||||
*
|
||||
* - [Azure Active Directory B2C documentation](https://learn.microsoft.com/en-us/azure/active-directory-b2c)
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* ## 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.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The Azure AD B2C provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/azure-ad-b2c.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 AzureADB2C(
|
||||
options: OIDCUserConfig<AzureADB2CProfile> & {
|
||||
primaryUserFlow?: string
|
||||
tenantId?: string
|
||||
}
|
||||
): OAuthConfig<P> {
|
||||
): OIDCConfig<AzureADB2CProfile> {
|
||||
const { tenantId, primaryUserFlow } = options
|
||||
options.issuer ??= `https://${tenantId}.b2clogin.com/${tenantId}.onmicrosoft.com/${primaryUserFlow}/v2.0`
|
||||
return {
|
||||
|
||||
99
packages/core/src/providers/beyondidentity.ts
Normal file
99
packages/core/src/providers/beyondidentity.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#5077c5", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Beyond Identity</b> integration.</span>
|
||||
* <a href="https://www.beyondidentity.com/">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/beyondidentity-dark.svg" height="48" width="48"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* ---
|
||||
* @module providers/beyondidentity
|
||||
*/
|
||||
|
||||
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
|
||||
|
||||
/** @see [Beyond Identity Developer Docs](https://developer.beyondidentity.com/) */
|
||||
export interface BeyondIdentityProfile {
|
||||
/** The user's unique identifier. */
|
||||
sub: string
|
||||
/** The user's full name. */
|
||||
name: string
|
||||
/** The user's preferred username. */
|
||||
preferred_username: string
|
||||
/** The user's email address. */
|
||||
email: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Beyond Identity login to your page.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```ts
|
||||
* import { Auth } from "@auth/core"
|
||||
* import BeyondIdentity from "@auth/core/providers/beyondidentity"
|
||||
*
|
||||
* const request = new Request("https://example.com")
|
||||
* const response = await Auth(request, {
|
||||
* providers: [BeyondIdentity({ clientId: "", clientSecret: "", issuer: "" })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* ## Resources
|
||||
*
|
||||
* - [Beyond Identity Developer Docs](https://developer.beyondidentity.com/)
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* ## 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.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The BeyondIdentity provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/beyondidentity.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 BeyondIdentity(
|
||||
config: OIDCUserConfig<BeyondIdentityProfile>
|
||||
): OIDCConfig<BeyondIdentityProfile> {
|
||||
return {
|
||||
id: "beyondidentity",
|
||||
name: "Beyond Identity",
|
||||
type: "oidc",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
email: profile.email,
|
||||
name: profile.name,
|
||||
image: null,
|
||||
preferred_username: profile.preferred_username,
|
||||
}
|
||||
},
|
||||
style: {
|
||||
logo: "/beyondidentity.svg",
|
||||
logoDark: "/beyondidentity-dark.svg",
|
||||
bg: "#fff",
|
||||
bgDark: "#5077c5",
|
||||
text: "#5077c5",
|
||||
textDark: "#fff",
|
||||
},
|
||||
options: config,
|
||||
}
|
||||
}
|
||||
@@ -129,14 +129,14 @@ export default function GitHub(
|
||||
url: "https://api.github.com/user",
|
||||
async request({ tokens, provider }) {
|
||||
const profile = await fetch(provider.userinfo?.url as URL, {
|
||||
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
||||
headers: { Authorization: `Bearer ${tokens.access_token}`, 'User-Agent': 'authjs' },
|
||||
}).then(async (res) => await res.json())
|
||||
|
||||
if (!profile.email) {
|
||||
// If the user does not have a public email, get another via the GitHub API
|
||||
// See https://docs.github.com/en/rest/users/emails#list-public-email-addresses-for-the-authenticated-user
|
||||
const res = await fetch("https://api.github.com/user/emails", {
|
||||
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
||||
headers: { Authorization: `Bearer ${tokens.access_token}`, 'User-Agent': 'authjs' },
|
||||
})
|
||||
|
||||
if (res.ok) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@auth/solid-start",
|
||||
"description": "Authentication for SolidStart.",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"client.*",
|
||||
@@ -30,22 +30,17 @@
|
||||
"devDependencies": {
|
||||
"@auth/core": "workspace:*",
|
||||
"@solidjs/meta": "^0.28.0",
|
||||
"@types/cookie": "0.5.1",
|
||||
"@types/node": "^18.7.14",
|
||||
"@types/set-cookie-parser": "^2.4.2",
|
||||
"next-auth": "workspace:*",
|
||||
"solid-js": "^1.5.7",
|
||||
"solid-start": "^0.2.1",
|
||||
"solid-start": "^0.2.14",
|
||||
"tsup": "^6.5.0",
|
||||
"typescript": "^4.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@auth/core": "~0.2.2 || ^0.2.2",
|
||||
"solid-js": "^1.5.7",
|
||||
"solid-start": "^0.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"set-cookie-parser": "^2.5.1"
|
||||
"solid-start": "^0.2.14"
|
||||
},
|
||||
"keywords": [
|
||||
"SolidJS",
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Auth } from "@auth/core"
|
||||
import { Cookie, parseString, splitCookiesString } from "set-cookie-parser"
|
||||
import { serialize } from "cookie"
|
||||
import type { AuthAction, AuthConfig, Session } from "@auth/core/types"
|
||||
|
||||
export interface SolidAuthConfig extends AuthConfig {
|
||||
@@ -22,26 +20,6 @@ const actions: AuthAction[] = [
|
||||
"error",
|
||||
]
|
||||
|
||||
// currently multiple cookies are not supported, so we keep the next-auth.pkce.code_verifier cookie for now:
|
||||
// because it gets updated anyways
|
||||
// src: https://github.com/solidjs/solid-start/issues/293
|
||||
const getSetCookieCallback = (cook?: string | null): Cookie | undefined => {
|
||||
if (!cook) return
|
||||
const splitCookie = splitCookiesString(cook)
|
||||
for (const cookName of [
|
||||
"__Secure-next-auth.session-token",
|
||||
"next-auth.session-token",
|
||||
"next-auth.pkce.code_verifier",
|
||||
"__Secure-next-auth.pkce.code_verifier",
|
||||
]) {
|
||||
const temp = splitCookie.find((e) => e.startsWith(`${cookName}=`))
|
||||
if (temp) {
|
||||
return parseString(temp)
|
||||
}
|
||||
}
|
||||
return parseString(splitCookie?.[0] ?? "") // just return the first cookie if no session token is found
|
||||
}
|
||||
|
||||
function SolidAuthHandler(prefix: string, authOptions: SolidAuthConfig) {
|
||||
return async (event: any) => {
|
||||
const { request } = event
|
||||
@@ -54,19 +32,7 @@ function SolidAuthHandler(prefix: string, authOptions: SolidAuthConfig) {
|
||||
return
|
||||
}
|
||||
|
||||
const res = await Auth(request, authOptions)
|
||||
if (["callback", "signin", "signout"].includes(action)) {
|
||||
const parsedCookie = getSetCookieCallback(
|
||||
res.clone().headers.get("Set-Cookie")
|
||||
)
|
||||
if (parsedCookie) {
|
||||
res.headers.set(
|
||||
"Set-Cookie",
|
||||
serialize(parsedCookie.name, parsedCookie.value, parsedCookie as any)
|
||||
)
|
||||
}
|
||||
}
|
||||
return res
|
||||
return await Auth(request, authOptions)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/sveltekit",
|
||||
"version": "0.2.1",
|
||||
"version": "0.3.0",
|
||||
"description": "Authentication for SvelteKit.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
@@ -69,4 +69,4 @@
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,23 @@
|
||||
* providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* or to use sveltekit platform environment variables for platforms like Cloudflare
|
||||
*
|
||||
* ```ts title="src/hooks.server.ts"
|
||||
* import { SvelteKitAuth } from "@auth/sveltekit"
|
||||
* import GitHub from "@auth/core/providers/github"
|
||||
* import type { Handle } from "@sveltejs/kit";
|
||||
*
|
||||
* export const handle = SvelteKitAuth(async (event) => {
|
||||
* const authOptions = {
|
||||
* providers: [GitHub({ clientId: event.platform.env.GITHUB_ID, clientSecret: event.platform.env.GITHUB_SECRET })]
|
||||
* secret: event.platform.env.AUTH_SECRET,
|
||||
* trustHost: true
|
||||
* }
|
||||
* return authOptions
|
||||
* }) satisfies Handle;
|
||||
* ```
|
||||
*
|
||||
* Don't forget to set the `AUTH_SECRET` [environment variable](https://kit.svelte.dev/docs/modules#$env-dynamic-private). This should be a minimum of 32 characters, random string. On UNIX systems you can use `openssl rand -hex 32` or check out `https://generate-secret.vercel.app/32`.
|
||||
*
|
||||
@@ -71,7 +88,7 @@
|
||||
* ## Managing the session
|
||||
*
|
||||
* The above example checks for a session available in `$page.data.session`, however that needs to be set by us somewhere.
|
||||
* If you want this data to be available to all your routes you can add this to your root `+layout.server.ts` file.
|
||||
* If you want this data to be available to all your routes you can add this to `src/routes/+layout.server.ts`.
|
||||
* The following code sets the session data in the `$page` store to be available to all routes.
|
||||
*
|
||||
* ```ts
|
||||
@@ -187,7 +204,7 @@
|
||||
*/
|
||||
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
import type { Handle } from "@sveltejs/kit"
|
||||
import type { Handle, RequestEvent } from "@sveltejs/kit"
|
||||
|
||||
import { dev } from "$app/environment"
|
||||
import { env } from "$env/dynamic/private"
|
||||
@@ -237,8 +254,15 @@ const actions: AuthAction[] = [
|
||||
"error",
|
||||
]
|
||||
|
||||
function AuthHandle(prefix: string, authOptions: AuthConfig): Handle {
|
||||
return function ({ event, resolve }) {
|
||||
type DynamicSvelteKitAuthConfig = (event: RequestEvent) => PromiseLike<SvelteKitAuthConfig>
|
||||
|
||||
function AuthHandle(svelteKitAuthOptions: SvelteKitAuthConfig | DynamicSvelteKitAuthConfig): Handle {
|
||||
return async function ({ event, resolve }) {
|
||||
const authOptions =
|
||||
typeof svelteKitAuthOptions === "object"
|
||||
? svelteKitAuthOptions
|
||||
: await svelteKitAuthOptions(event)
|
||||
const { prefix = "/auth" } = authOptions
|
||||
const { url, request } = event
|
||||
|
||||
event.locals.getSession ??= () => getSession(request, authOptions)
|
||||
@@ -259,11 +283,12 @@ function AuthHandle(prefix: string, authOptions: AuthConfig): Handle {
|
||||
* The main entry point to `@auth/sveltekit`
|
||||
* @see https://sveltekit.authjs.dev
|
||||
*/
|
||||
export function SvelteKitAuth(options: SvelteKitAuthConfig): Handle {
|
||||
const { prefix = "/auth", ...authOptions } = options
|
||||
authOptions.secret ??= env.AUTH_SECRET
|
||||
authOptions.trustHost ??= !!(env.AUTH_TRUST_HOST ?? env.VERCEL ?? dev)
|
||||
return AuthHandle(prefix, authOptions)
|
||||
export function SvelteKitAuth(options: SvelteKitAuthConfig | DynamicSvelteKitAuthConfig): Handle {
|
||||
if (typeof options === "object") {
|
||||
options.secret ??= env.AUTH_SECRET
|
||||
options.trustHost ??= !!(env.AUTH_TRUST_HOST ?? env.VERCEL ?? dev)
|
||||
}
|
||||
return AuthHandle(options)
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -189,6 +189,20 @@ We're happy to announce we've recently created an [OpenCollective](https://openc
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<a href="https://stytch.com" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/69983493?s=200&v=4" alt="Stytch Logo" />
|
||||
</a><br />
|
||||
<div>Stytch</div><br />
|
||||
<sub>🥈 Silver Financial Sponsor</sub>
|
||||
</td>
|
||||
<td align="center" valign="top">
|
||||
<a href="https://beyondidentity.com" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/69811361?s=200&v=4" alt="Beyond Identity Logo" />
|
||||
</a><br />
|
||||
<div>Beyond Identity</div><br />
|
||||
<sub>🥈 Silver Financial Sponsor</sub>
|
||||
</td>
|
||||
<td align="center" valign="top">
|
||||
<a href="https://vercel.com" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/14985020?v=4" alt="Vercel Logo" />
|
||||
|
||||
1575
pnpm-lock.yaml
generated
1575
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user