diff --git a/.github/ISSUE_TEMPLATE/2_bug_provider.yml b/.github/ISSUE_TEMPLATE/2_bug_provider.yml index d129a423c8..1305028464 100644 --- a/.github/ISSUE_TEMPLATE/2_bug_provider.yml +++ b/.github/ISSUE_TEMPLATE/2_bug_provider.yml @@ -74,6 +74,7 @@ body: - "Netlify" - "NetSuite" - "Nextcloud" + - "NotificationAPI" - "Notion" - "Okta" - "OneLogin" diff --git a/docs/pages/getting-started/authentication/email.mdx b/docs/pages/getting-started/authentication/email.mdx index 7e0248efda..1447e650aa 100644 --- a/docs/pages/getting-started/authentication/email.mdx +++ b/docs/pages/getting-started/authentication/email.mdx @@ -41,6 +41,14 @@ This login mechanism starts by the user providing their email address at the log
Forward Email
+ + +
NotificationAPI
+
+ + +### NotificationAPI Setup + + + +### Database Adapter + +Please make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier, +a database is required for passwordless login to work as verification tokens need to be stored. + +### Setup Environment Variables + +Auth.js will automatically pick up these if formatted like the example below. +You can [also use a different name for the environment variables](/guides/environment-variables#oauth-variables) if needed, but then you’ll need to pass them to the provider manually. + +```bash filename=".env" +NOTIFICATIONAPI_API_KEY=... +``` + +### Setup Provider + +Let’s enable `NotificationAPI` as a sign in option in our Auth.js configuration: + + + + +```ts filename="./auth.ts" +import NextAuth from "next-auth" +import NotificationAPI from "next-auth/providers/notificationapi" + +export const { handlers, auth, signIn, signOut } = NextAuth({ + providers: [ + NotificationAPI({ + notificationId: "auth_magic_link", + }), + ], +}) +``` + + + + +```ts filename="/src/routes/plugin@auth.ts" +import { QwikAuth$ } from "@auth/qwik" +import NotificationAPI from "@auth/qwik/providers/notificationapi" + +export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$( + () => ({ + providers: [ + NotificationAPI({ + notificationId: "auth_magic_link", + }), + ], + }) +) +``` + + + + +```ts filename="./src/auth.ts" +import SvelteKitAuth from "@auth/sveltekit" +import NotificationAPI from "@auth/sveltekit/providers/notificationapi" + +export const { handle, signIn, signOut } = SvelteKitAuth({ + providers: [ + NotificationAPI({ + notificationId: "auth_magic_link", + }), + ], +}) +``` + +```ts filename="./src/hooks.server.ts" +export { handle } from "./auth" +``` + + + + +### Add Signin Button + +Next, we can add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in. + + + + +```tsx filename="./components/sign-in.tsx" +import { signIn } from "../../auth.ts" + +export function SignIn() { + return ( +
{ + "use server" + await signIn("notificationapi", formData) + }} + > + + +
+ ) +} +``` + +
+ + +```tsx filename="./components/sign-in.tsx" +"use client" +import { signIn } from "next-auth/react" + +export function SignIn() { + const notificationapiAction = (formData: FormData) => { + signIn("notificationapi", formData) + } + + return ( +
+ + +
+ ) +} +``` + +
+ + +```ts filename="./components/sign-in.tsx" +import { component$ } from "@builder.io/qwik" +import { useSignIn } from "./plugin@auth" + +export default component$(() => { + const signInSig = useSignIn() + + return ( + + ) +}) +``` + + + + +```html filename="src/routes/+page.svelte" + + +
+ +
+``` + +
+
+ +### Signin + +Start your application, once the user enters their Email and clicks on the signin button, they'll be redirected to a page that asks them to check their email. When they click on the link in their email, they will be signed in. + +
+ + + You can visually design your magic link email template in NotificationAPI. + + +For more information on this provider go to the [NotificationAPI provider docs page](/getting-started/providers/notificationapi). + +
+ + +# NotificationAPI Provider + +## Overview + +The [NotificationAPI](https://www.notificationapi.com) provider for Auth.js is an easy way to implement the "magic link" authentication flow with Email. It doesn't require additional integrations or writing HTML email templates. + +This flow can be used to sign in existing or new users by simply sending them a link. + +## Flow + +1. User inputs their email. +2. Auth.js generates a verification token - valid for 24 hours by default - and stores it using the database adapter. +3. NotificationAPI delivers the "Magic Link" via Email. The Magic Link is a URL containing the verification token. When visited, the token is passed to Auth.js which logs the user in. + + + The magic link auth flow requires a [database + adapter](https://authjs.dev/getting-started/database) to work. The database is + used to store the verification tokens, to be matched later against what the + user provides. + + +## Setup + +### 1. NotificationAPI Account + +Create a [NotificationAPI account](https://app.notificationapi.com) and design a magic link email notification in the dashboard. + +Make sure to include the `{{url}}` parameter in the email design. This `{{url}}` tag will be dynamically replaced with the real URL. + +### 2. Auth.js Configuration + +Set up environment variables using the values from NotificationAPI's environments page: + +```sh +NOTIFICATIONAPI_API_KEY=... +``` + +If you name your environment variable like the above, the provider will pick it up automatically. + + + + +```ts filename="./auth.ts" +import NextAuth from "next-auth" +import NotificationAPI from "next-auth/providers/notificationapi" + +export const { handlers, auth, signIn, signOut } = NextAuth({ + adapter: ..., // you need an adapter for magic link auth flows + providers: [ + NotificationAPI({ + // The ID of the notification you designed in the NotificationAPI dashboard: + notificationId: "auth_magic_link" + }), + ], +}) +``` + + + + +```ts filename="/src/routes/plugin@auth.ts" +import { QwikAuth$ } from "@auth/qwik" +import NotificationAPI from "@auth/qwik/providers/notificationapi" + +export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$( + () => ({ + adapter: ..., // you need an adapter for magic link auth flows + providers: [ + NotificationAPI({ + // The ID of the notification you designed in the NotificationAPI dashboard: + notificationId: "auth_magic_link" + }), + ], + +}) +) + +```` + + + + +```ts filename="./src/auth.ts" +import { SvelteKitAuth } from "@auth/sveltekit" +import NotificationAPI from "@auth/sveltekit/providers/notificationapi" + +export const { handle, signIn, signOut } = SvelteKitAuth({ + adapter: ..., // you need an adapter for magic link auth flows + providers: [ + NotificationAPI({ + // The ID of the notification you designed in the NotificationAPI dashboard: + notificationId: "auth_magic_link" + }), + ], +}) +```` + + + diff --git a/docs/public/img/providers/notificationapi.svg b/docs/public/img/providers/notificationapi.svg new file mode 100644 index 0000000000..4d1ad2848b --- /dev/null +++ b/docs/public/img/providers/notificationapi.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/core/scripts/generate-providers.js b/packages/core/scripts/generate-providers.js index ff74012730..ccb5da97fa 100644 --- a/packages/core/scripts/generate-providers.js +++ b/packages/core/scripts/generate-providers.js @@ -11,6 +11,7 @@ const emailProvidersFile = [ "forwardemail", "mailgun", "nodemailer", + "notificationapi", "passkey", "postmark", "resend", diff --git a/packages/core/src/providers/notificationapi.ts b/packages/core/src/providers/notificationapi.ts new file mode 100644 index 0000000000..8ace255402 --- /dev/null +++ b/packages/core/src/providers/notificationapi.ts @@ -0,0 +1,121 @@ +/** + *
+ *
+ * + * The NotificationAPI provider helps you send magic links through email from Auth.js.
+ *
+ *
+ * + * + * + *
+ * + * @module providers/notificationapi + */ + +import type { EmailConfig } from "./index.js" + +/** + * + * ### Setup + * + * #### Configuration + *```ts + * NextAuth({ + * ... + * providers: [ + * NotificationAPI({ + * notificationId: 'auth_magic_link' // from dashboard + * }) + * ] + * ... + * }) + * ``` + * + * ### Resources + * + * - [NotificationAPI Documentation](https://docs.notificationapi.com) + * + * :::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 type NotificationAPIUserConfig = { + notificationId: string + apiKey?: string + baseURL?: string +} + +export interface NotificationAPIConfig + extends Omit< + EmailConfig, + "sendVerificationRequest" | "options" | "apiKey" | "from" + > { + id: string + apiKey?: string + notificationId?: string + baseURL: string + sendVerificationRequest: (params: Params) => Promise +} + +type Params = Parameters[0] & { + provider: NotificationAPIConfig +} + +export default function NotificationAPI( + config: NotificationAPIUserConfig +): NotificationAPIConfig { + return { + id: "notificationapi", + type: "email", + name: "NotificationAPI", + notificationId: config.notificationId, + baseURL: config.baseURL || "https://api.notificationapi.com", + apiKey: config.apiKey || process.env.NOTIFICATIONAPI_API_KEY, + maxAge: 24 * 60 * 60, + async sendVerificationRequest(params: Params) { + const { identifier, provider, url, token } = params + const { apiKey, notificationId, baseURL } = provider + + if (!apiKey) { + throw new Error( + "Missing NOTIFICATIONAPI_API_KEY. Please add to .env file or pass as `apiKey` in the provider configuration." + ) + } + + const [clientId, clientSecret] = atob(apiKey).split(":") + const res = await fetch(`${baseURL}/${clientId}/sender`, { + method: "POST", + headers: { + Authorization: `Basic ${btoa(`${clientId}:${clientSecret}`)}`, + "Content-Type": "application/json", + "User-Agent": "Auth.js/v0.38.0", + Accept: "application/json", + }, + body: JSON.stringify({ + notificationId: notificationId, + user: { + id: identifier, + email: identifier, + }, + mergeTags: { + url, + token, + }, + }), + }) + + if (!res.ok) + throw new Error( + "NotificationAPI Error: " + JSON.stringify(await res.json()) + ) + }, + } +}