You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 7-8.RSVP
Socket
Socket
Sign inDemoInstall

@auth/core

Package Overview
Dependencies
7
Maintainers
2
Versions
90
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.22.0 to 0.23.0

providers/nodemailer.d.ts

1

index.js

@@ -121,2 +121,3 @@ /**

const page = (isAuthError && error.kind) || "error";
// TODO: Filter out some error types from being sent to the client
const params = new URLSearchParams({ error: type });

@@ -123,0 +124,0 @@ const path = config.pages?.[page] ?? `${config.basePath}/${page.toLowerCase()}`;

6

jwt.d.ts

@@ -8,4 +8,4 @@ /**

*
* The JWT issued by Auth.js is _encrypted by default_, using the _A256GCM_ algorithm ({@link https://www.rfc-editor.org/rfc/rfc7516 JWE}).
* It uses the `AUTH_SECRET` environment variable to derive a sufficient encryption key.
* The JWT issued by Auth.js is _encrypted by default_, using the _A256CBC-HS512_ algorithm ({@link https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5 JWE}).
* It uses the `AUTH_SECRET` environment variable or the passed `secret` propery to derive a suitable encryption key.
*

@@ -41,3 +41,3 @@ * :::info Note

import type { LoggerInstance } from "./lib/utils/logger.js";
/** Issues a JWT. By default, the JWT is encrypted using "A256GCM". */
/** Issues a JWT. By default, the JWT is encrypted using "A256CBC-HS512". */
export declare function encode<Payload = JWT>(params: JWTEncodeParams<Payload>): Promise<string>;

@@ -44,0 +44,0 @@ /** Decodes a Auth.js issued JWT. */

@@ -8,4 +8,4 @@ /**

*
* The JWT issued by Auth.js is _encrypted by default_, using the _A256GCM_ algorithm ({@link https://www.rfc-editor.org/rfc/rfc7516 JWE}).
* It uses the `AUTH_SECRET` environment variable to derive a sufficient encryption key.
* The JWT issued by Auth.js is _encrypted by default_, using the _A256CBC-HS512_ algorithm ({@link https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5 JWE}).
* It uses the `AUTH_SECRET` environment variable or the passed `secret` propery to derive a suitable encryption key.
*

@@ -46,9 +46,11 @@ * :::info Note

const now = () => (Date.now() / 1000) | 0;
/** Issues a JWT. By default, the JWT is encrypted using "A256GCM". */
const alg = "dir";
const enc = "A256CBC-HS512";
/** Issues a JWT. By default, the JWT is encrypted using "A256CBC-HS512". */
export async function encode(params) {
const { token = {}, secret, maxAge = DEFAULT_MAX_AGE, salt } = params;
const encryptionSecret = await getDerivedEncryptionKey(secret, salt);
const encryptionSecret = await getDerivedEncryptionKey(enc, secret, salt);
// @ts-expect-error `jose` allows any object as payload.
return await new EncryptJWT(token)
.setProtectedHeader({ alg: "dir", enc: "A256GCM" })
.setProtectedHeader({ alg, enc })
.setIssuedAt()

@@ -64,5 +66,6 @@ .setExpirationTime(now() + maxAge)

return null;
const encryptionSecret = await getDerivedEncryptionKey(secret, salt);
const { payload } = await jwtDecrypt(token, encryptionSecret, {
const { payload } = await jwtDecrypt(token, async ({ enc }) => await getDerivedEncryptionKey(enc, secret, salt), {
clockTolerance: 15,
keyManagementAlgorithms: [alg],
contentEncryptionAlgorithms: [enc, "A256GCM"]
});

@@ -98,4 +101,15 @@ return payload;

}
async function getDerivedEncryptionKey(keyMaterial, salt) {
return await hkdf("sha256", keyMaterial, salt, `Auth.js Generated Encryption Key (${salt})`, 32);
async function getDerivedEncryptionKey(enc, keyMaterial, salt) {
let length;
switch (enc) {
case "A256CBC-HS512":
length = 64;
break;
case "A256GCM":
length = 32;
break;
default:
throw new Error("Unsupported JWT Content Encryption Algorithm");
}
return await hkdf("sha256", keyMaterial, salt, `Auth.js Generated Encryption Key (${salt})`, length);
}

@@ -16,3 +16,3 @@ import type { InternalOptions, Profile, RequestInternal } from "../../../../types.js";

user?: {
id: string;
id: `${string}-${string}-${string}-${string}-${string}`;
email: string | undefined;

@@ -31,3 +31,3 @@ name?: string | null | undefined;

scope?: string | undefined;
token_type?: string | undefined;
token_type?: "bearer" | "dpop" | Lowercase<string> | undefined;
expires_at?: number | undefined;

@@ -43,3 +43,3 @@ } | {

scope?: string | undefined;
token_type?: string | undefined;
token_type?: "bearer" | "dpop" | Lowercase<string> | undefined;
expires_at?: number | undefined;

@@ -46,0 +46,0 @@ } | undefined;

@@ -115,3 +115,3 @@ import * as checks from "./checks.js";

...userFromProfile,
id: userFromProfile.id?.toString() ?? crypto.randomUUID(),
id: crypto.randomUUID(),
email: userFromProfile.email?.toLowerCase(),

@@ -118,0 +118,0 @@ };

@@ -88,8 +88,6 @@ import { JWTSessionError, SessionTokenError } from "../../errors.js";

const sessionPayload = await callbacks.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 },
expires: session.expires.toISOString(),
},
// TODO: user already passed below,
// remove from session object in https://github.com/nextauthjs/next-auth/pull/9702
// @ts-expect-error
session: { ...session, user },
user,

@@ -96,0 +94,0 @@ newSession,

@@ -39,2 +39,3 @@ import { createHash, randomString, toRequest } from "../../utils/web.js";

const secret = provider.secret ?? options.secret;
const baseUrl = new URL(options.basePath, options.url.origin);
const sendRequest = provider.sendVerificationRequest({

@@ -44,3 +45,3 @@ identifier: email,

expires,
url: `${url}/callback/${provider.id}?${new URLSearchParams({
url: `${baseUrl}/callback/${provider.id}?${new URLSearchParams({
callbackUrl,

@@ -61,3 +62,3 @@ token,

return {
redirect: `${url}/verify-request?${new URLSearchParams({
redirect: `${baseUrl}/verify-request?${new URLSearchParams({
provider: provider.id,

@@ -64,0 +65,0 @@ type: provider.type,

@@ -21,3 +21,10 @@ import * as jwt from "../jwt.js";

session({ session }) {
return session;
return {
user: {
name: session.user?.name,
email: session.user?.email,
image: session.user?.image,
},
expires: session.expires?.toISOString?.() ?? session.expires,
};
},

@@ -98,3 +105,3 @@ jwt({ token }) {

...authOptions.experimental,
}
},
};

@@ -101,0 +108,0 @@ // Init cookies

@@ -1,2 +0,1 @@

import { OAuthProfileParseError } from "../../errors.js";
import { merge } from "./merge.js";

@@ -58,12 +57,10 @@ /**

* Returns basic user profile from the userinfo response/`id_token` claims.
* An `id` is generated internally (using `crypto.randomUUID()`) and will override `id` if provided.
* The result if this function is user to create the `User` in the database.
* @see https://authjs.dev/reference/core/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
* @see https://openid.net/specs/openid-connect-core-1_0.html#
*/
const defaultProfile = (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,

@@ -70,0 +67,0 @@ email: profile.email,

@@ -100,4 +100,4 @@ import { parse as parseCookie, serialize } from "cookie";

export function parseActionAndProviderId(pathname, base) {
const a = pathname.split(base);
if (a.length !== 2 || a[0] !== "")
const a = pathname.match(new RegExp(`^${base}(.+)`));
if (a === null)
throw new UnknownAction(`Cannot parse action at ${pathname}`);

@@ -104,0 +104,0 @@ const [_, actionAndProviderId] = a;

{
"name": "@auth/core",
"version": "0.22.0",
"version": "0.23.0",
"description": "Authentication for the Web.",

@@ -88,4 +88,4 @@ "keywords": [

"postcss-nested": "6.0.0",
"vite": "^5.0.2",
"vitest": "^0.25.3"
"vite": "^5.0.12",
"vitest": "^1.2.1"
},

@@ -92,0 +92,0 @@ "scripts": {

@@ -17,3 +17,3 @@ /**

/** See the [available regions](https://develop.battle.net/documentation/guides/regionality-and-apis) */
export type BattleNetIssuer = "https://www.battlenet.com.cn/oauth" | `https://${"us" | "eu" | "kr" | "tw"}.battle.net/oauth`;
export type BattleNetIssuer = "https://oauth.battle.net" | "https://oauth.battlenet.com.cn" | "https://www.battlenet.com.cn/oauth" | `https://${"us" | "eu" | "kr" | "tw"}.battle.net/oauth`;
/**

@@ -42,2 +42,4 @@ * Add Battle.net login to your page.

* type BattleNetIssuer =
* | "https://oauth.battle.net"
* | "https://oauth.battlenet.com.cn"
* | "https://www.battlenet.com.cn/oauth"

@@ -44,0 +46,0 @@ * | "https://us.battle.net/oauth"

@@ -24,2 +24,4 @@ /**

* type BattleNetIssuer =
* | "https://oauth.battle.net"
* | "https://oauth.battlenet.com.cn"
* | "https://www.battlenet.com.cn/oauth"

@@ -26,0 +28,0 @@ * | "https://us.battle.net/oauth"

@@ -21,4 +21,6 @@ /**

username: string;
/** the user's 4-digit discord-tag */
/** the user's Discord-tag */
discriminator: string;
/** the user's display name, if it is set */
global_name: string | null;
/**

@@ -25,0 +27,0 @@ * the user's avatar hash:

@@ -59,3 +59,5 @@ /**

if (profile.avatar === null) {
const defaultAvatarNumber = parseInt(profile.discriminator) % 5;
const defaultAvatarNumber = profile.discriminator === "0"
? Number(BigInt(profile.id) >> BigInt(22)) % 6
: parseInt(profile.discriminator) % 5;
profile.image_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNumber}.png`;

@@ -69,3 +71,3 @@ }

id: profile.id,
name: profile.username,
name: profile.global_name ?? profile.username,
email: profile.email,

@@ -72,0 +74,0 @@ image: profile.image_url,

import type { CommonProviderOptions } from "./index.js";
import type { Awaitable, Theme } from "../types.js";
import { Transport, TransportOptions } from "nodemailer";
import * as JSONTransport from "nodemailer/lib/json-transport/index.js";
import * as SendmailTransport from "nodemailer/lib/sendmail-transport/index.js";
import * as SESTransport from "nodemailer/lib/ses-transport/index.js";
import * as SMTPTransport from "nodemailer/lib/smtp-transport/index.js";
import * as SMTPPool from "nodemailer/lib/smtp-pool/index.js";
import * as StreamTransport from "nodemailer/lib/stream-transport/index.js";
type AllTransportOptions = string | SMTPTransport | SMTPTransport.Options | SMTPPool | SMTPPool.Options | SendmailTransport | SendmailTransport.Options | StreamTransport | StreamTransport.Options | JSONTransport | JSONTransport.Options | SESTransport | SESTransport.Options | Transport<any> | TransportOptions;
export interface SendVerificationRequestParams {
identifier: string;
url: string;
expires: Date;
provider: EmailConfig;
token: string;
theme: Theme;
request: Request;
}
import type { NodemailerConfig, NodemailerUserConfig } from "./nodemailer.js";
/**
* The Email Provider needs to be configured with an e-mail client.
* By default, it uses `nodemailer`, which you have to install if this
* provider is present.
* @deprecated
*
* You can use a other services as well, like:
* - [Postmark](https://postmarkapp.com)
* - [Mailgun](https://www.mailgun.com)
* - [SendGrid](https://sendgrid.com)
* - etc.
* Import this provider from the `providers/nodemailer` submodule instead of `providers/email`.
*
* [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
* To log in with nodemailer, change `signIn("email")` to `signIn("nodemailer")`
*/
export interface EmailUserConfig extends Record<string, unknown> {
server?: AllTransportOptions;
type?: "email";
/** @default `"Auth.js <no-reply@authjs.dev>"` */
from?: string;
/**
* How long until the e-mail can be used to log the user in,
* in seconds. Defaults to 1 day
*
* @default 86400
*/
maxAge?: number;
/** [Documentation](https://authjs.dev/guides/providers/email#customizing-emails) */
sendVerificationRequest?: (params: SendVerificationRequestParams) => Awaitable<void>;
/**
* By default, we are generating a random verification token.
* You can make it predictable or modify it as you like with this method.
*
* @example
* ```ts
* Providers.Email({
* async generateVerificationToken() {
* return "ABC123"
* }
* })
* ```
* [Documentation](https://authjs.dev/guides/providers/email#customizing-the-verification-token)
*/
generateVerificationToken?: () => Awaitable<string>;
/** If defined, it is used to hash the verification token when saving to the database . */
secret?: string;
/**
* Normalizes the user input before sending the verification request.
*
* ⚠️ Always make sure this method returns a single email address.
*
* @note Technically, the part of the email address local mailbox element
* (everything before the `@` symbol) should be treated as 'case sensitive'
* according to RFC 2821, but in practice this causes more problems than
* it solves, e.g.: when looking up users by e-mail from databases.
* By default, we treat email addresses as all lower case,
* but you can override this function to change this behavior.
*
* [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;
}
export default function Email(config: NodemailerUserConfig): NodemailerConfig;
export type EmailProviderType = "email";
export interface EmailConfig extends CommonProviderOptions {
id: "email";
type: "email";
name: "Email";
server: AllTransportOptions;
id: string;
type: EmailProviderType;
name: string;
from: string;
maxAge: number;
sendVerificationRequest: (params: SendVerificationRequestParams) => Awaitable<void>;
/**
* This is copied into EmailConfig in parseProviders() don't use elsewhere
*/
options: EmailUserConfig;
sendVerificationRequest: (params: {
identifier: string;
url: string;
expires: Date;
provider: EmailConfig;
token: string;
theme: Theme;
request: Request;
}) => Awaitable<void>;
/** Used to hash the verification token. */
secret?: string;
/** Used with HTTP-based email providers */
apiKey?: string;
generateVerificationToken?: () => Awaitable<string>;
normalizeIdentifier?: (identifier: string) => string;
options: EmailUserConfig;
}
export type EmailProviderType = "email";
export type EmailUserConfig = Omit<Partial<EmailConfig>, "options" | "type">;
/**
* ## 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.
* Email HTML body
* Insert invisible space into domains from being turned into a hyperlink by email
* clients like Outlook and Apple mail, as this is confusing because it seems
* like they are supposed to click on it to sign in.
*
* 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/core/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.
* :::
* @note We don't add the email address to avoid needing to escape it, if you do, remember to sanitize it!
*/
export default function Email(config: EmailUserConfig): EmailConfig;
export {};
export declare function html(params: {
url: string;
host: string;
theme: Theme;
}): string;
/** Email Text body (fallback for email clients that don't render HTML, e.g. feature phones) */
export declare function text({ url, host }: {
url: string;
host: string;
}): string;
//# sourceMappingURL=email.d.ts.map

@@ -1,259 +0,17 @@

import { createTransport } from "nodemailer";
// TODO: Kepts for backwards compatibility
// Remove this import and encourage users
// to import it from @auth/core/providers/nodemailer directly
import Nodemailer from "./nodemailer.js";
/**
* ## 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.
* @deprecated
*
* 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).
* Import this provider from the `providers/nodemailer` submodule instead of `providers/email`.
*
* 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/core/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.
* :::
* To log in with nodemailer, change `signIn("email")` to `signIn("nodemailer")`
*/
export default function Email(config) {
return {
...Nodemailer(config),
id: "email",
type: "email",
name: "Email",
server: { host: "localhost", port: 25, auth: { user: "", pass: "" } },
from: "Auth.js <no-reply@authjs.dev>",
maxAge: 24 * 60 * 60,
async sendVerificationRequest(params) {
const { identifier, url, provider, theme } = params;
const { host } = new URL(url);
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 (${failed.join(", ")}) could not be sent`);
}
},
options: config,
};

@@ -269,3 +27,3 @@ }

*/
function html(params) {
export function html(params) {
const { url, host, theme } = params;

@@ -318,4 +76,4 @@ const escapedHost = host.replace(/\./g, "&#8203;.");

/** Email Text body (fallback for email clients that don't render HTML, e.g. feature phones) */
function text({ url, host }) {
export function text({ url, host }) {
return `Sign in to ${host}\n${url}\n\n`;
}

@@ -1,2 +0,2 @@

export type OAuthProviderType = "42-school" | "apple" | "asgardeo" | "atlassian" | "auth0" | "authentik" | "azure-ad-b2c" | "azure-ad" | "azure-devops" | "battlenet" | "beyondidentity" | "box" | "boxyhq-saml" | "bungie" | "click-up" | "cognito" | "coinbase" | "descope" | "discord" | "dribbble" | "dropbox" | "duende-identity-server6" | "eveonline" | "facebook" | "faceit" | "foursquare" | "freshbooks" | "fusionauth" | "github" | "gitlab" | "google" | "hubspot" | "identity-server4" | "instagram" | "kakao" | "keycloak" | "line" | "linkedin" | "mailchimp" | "mailru" | "mastodon" | "mattermost" | "medium" | "naver" | "netlify" | "notion" | "okta" | "onelogin" | "osso" | "osu" | "passage" | "patreon" | "pinterest" | "pipedrive" | "reddit" | "salesforce" | "slack" | "spotify" | "strava" | "tiktok" | "todoist" | "trakt" | "twitch" | "twitter" | "united-effects" | "vk" | "wikimedia" | "wordpress" | "workos" | "yandex" | "zitadel" | "zoho" | "zoom";
export type OAuthProviderType = "42-school" | "apple" | "asgardeo" | "atlassian" | "auth0" | "authentik" | "azure-ad-b2c" | "azure-ad" | "azure-devops" | "battlenet" | "beyondidentity" | "box" | "boxyhq-saml" | "bungie" | "click-up" | "cognito" | "coinbase" | "descope" | "discord" | "dribbble" | "dropbox" | "duende-identity-server6" | "eveonline" | "facebook" | "faceit" | "foursquare" | "freshbooks" | "fusionauth" | "github" | "gitlab" | "google" | "hubspot" | "identity-server4" | "instagram" | "kakao" | "keycloak" | "line" | "linkedin" | "mailchimp" | "mailru" | "mastodon" | "mattermost" | "medium" | "naver" | "netlify" | "nodemailer" | "notion" | "okta" | "onelogin" | "ory-hydra" | "osso" | "osu" | "passage" | "patreon" | "pinterest" | "pipedrive" | "reddit" | "resend" | "salesforce" | "sendgrid" | "slack" | "spotify" | "strava" | "tiktok" | "todoist" | "trakt" | "twitch" | "twitter" | "united-effects" | "vk" | "wikimedia" | "wordpress" | "workos" | "yandex" | "zitadel" | "zoho" | "zoom";
//# sourceMappingURL=oauth-types.d.ts.map

@@ -12,255 +12,255 @@ /**

import type { OAuthConfig, OAuthUserConfig } from "./index.js";
/** https://dev.vk.com/reference/objects/user */
export interface VkProfile {
response: Array<{
id: number;
first_name: string;
last_name: string;
photo_100: string;
can_access_closed: boolean;
is_closed: boolean;
deactivated?: string;
sex?: 0 | 1 | 2;
screen_name?: string;
photo_50?: string;
online?: 0 | 1;
online_mobile?: 0 | 1;
online_app?: number;
verified?: 0 | 1;
trending?: 0 | 1;
friend_status?: 0 | 1 | 2 | 3;
first_name_nom?: string;
first_name_gen?: string;
first_name_dat?: string;
first_name_acc?: string;
first_name_ins?: string;
first_name_abl?: string;
last_name_nom?: string;
last_name_gen?: string;
last_name_dat?: string;
last_name_acc?: string;
last_name_ins?: string;
last_name_abl?: string;
nickname?: string;
maiden_name?: string;
domain?: string;
bdate?: string;
city?: {
id: number;
first_name: string;
last_name: string;
photo_100: string;
can_access_closed: boolean;
is_closed: boolean;
deactivated?: string;
sex?: 0 | 1 | 2;
screen_name?: string;
photo_50?: string;
online?: 0 | 1;
online_mobile?: 0 | 1;
online_app?: number;
verified?: 0 | 1;
trending?: 0 | 1;
friend_status?: 0 | 1 | 2 | 3;
first_name_nom?: string;
first_name_gen?: string;
first_name_dat?: string;
first_name_acc?: string;
first_name_ins?: string;
first_name_abl?: string;
last_name_nom?: string;
last_name_gen?: string;
last_name_dat?: string;
last_name_acc?: string;
last_name_ins?: string;
last_name_abl?: string;
nickname?: string;
maiden_name?: string;
domain?: string;
bdate?: string;
city?: {
id: number;
title: string;
};
country?: {
id: number;
title: string;
};
timezone?: number;
photo_200?: string;
photo_max?: string;
photo_200_orig?: string;
photo_400_orig?: string;
photo_max_orig?: string;
photo_id?: string;
has_photo?: 0 | 1;
has_mobile?: 0 | 1;
is_friend?: 0 | 1;
can_post?: 0 | 1;
can_see_all_posts?: 0 | 1;
can_see_audio?: 0 | 1;
connections?: {
facebook?: string;
skype?: string;
twitter?: string;
livejournal?: string;
instagram?: string;
};
photo_400?: string;
wall_default?: "owner" | "all";
interests?: string;
books?: string;
tv?: string;
quotes?: string;
about?: string;
games?: string;
movies?: string;
activities?: string;
music?: string;
can_write_private_message?: 0 | 1;
can_send_friend_request?: 0 | 1;
contacts?: {
mobile_phone?: string;
home_phone?: string;
};
site?: string;
status_audio?: {
title: string;
};
country?: {
id: number;
title: string;
};
timezone?: number;
photo_200?: string;
photo_max?: string;
photo_200_orig?: string;
photo_400_orig?: string;
photo_max_orig?: string;
photo_id?: string;
has_photo?: 0 | 1;
has_mobile?: 0 | 1;
is_friend?: 0 | 1;
can_post?: 0 | 1;
can_see_all_posts?: 0 | 1;
can_see_audio?: 0 | 1;
connections?: {
facebook?: string;
skype?: string;
twitter?: string;
livejournal?: string;
instagram?: string;
};
photo_400?: string;
wall_default?: "owner" | "all";
interests?: string;
books?: string;
tv?: string;
quotes?: string;
about?: string;
games?: string;
movies?: string;
activities?: string;
music?: string;
can_write_private_message?: 0 | 1;
can_send_friend_request?: 0 | 1;
contacts?: {
mobile_phone?: string;
home_phone?: string;
};
site?: string;
status_audio?: {
access_key?: string;
artist: string;
id: number;
owner_id: number;
title: string;
url?: string;
duration: number;
date?: number;
album_id?: number;
genre_id?: number;
performer?: string;
};
status?: string;
last_seen?: {
platform?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
time?: number;
};
exports?: {
facebook?: number;
livejournal?: number;
twitter?: number;
instagram?: number;
};
crop_photo?: {
photo: {
access_key?: string;
artist: string;
album_id: number;
date: number;
height?: number;
id: number;
owner_id: number;
title: string;
url?: string;
duration: number;
date?: number;
album_id?: number;
genre_id?: number;
performer?: string;
};
status?: string;
last_seen?: {
platform?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
time?: number;
};
exports?: {
facebook?: number;
livejournal?: number;
twitter?: number;
instagram?: number;
};
crop_photo?: {
photo: {
access_key?: string;
album_id: number;
date: number;
images?: Array<{
height?: number;
id: number;
images?: Array<{
height?: number;
type?: "s" | "m" | "x" | "l" | "o" | "p" | "q" | "r" | "y" | "z" | "w";
url?: string;
width?: number;
}>;
lat?: number;
long?: number;
owner_id: number;
photo_256?: string;
can_comment?: 0 | 1;
place?: string;
post_id?: number;
sizes?: Array<{
height: number;
url: string;
src?: string;
type: "s" | "m" | "x" | "o" | "p" | "q" | "r" | "k" | "l" | "y" | "z" | "c" | "w" | "a" | "b" | "e" | "i" | "d" | "j" | "temp" | "h" | "g" | "n" | "f" | "max";
width: number;
}>;
text?: string;
user_id?: number;
type?: "s" | "m" | "x" | "l" | "o" | "p" | "q" | "r" | "y" | "z" | "w";
url?: string;
width?: number;
has_tags: boolean;
};
crop: {
x: number;
y: number;
x2: number;
y2: number;
};
rect: {
x: number;
y: number;
x2: number;
y2: number;
};
}>;
lat?: number;
long?: number;
owner_id: number;
photo_256?: string;
can_comment?: 0 | 1;
place?: string;
post_id?: number;
sizes?: Array<{
height: number;
url: string;
src?: string;
type: "s" | "m" | "x" | "o" | "p" | "q" | "r" | "k" | "l" | "y" | "z" | "c" | "w" | "a" | "b" | "e" | "i" | "d" | "j" | "temp" | "h" | "g" | "n" | "f" | "max";
width: number;
}>;
text?: string;
user_id?: number;
width?: number;
has_tags: boolean;
};
followers_count?: number;
blacklisted?: 0 | 1;
blacklisted_by_me?: 0 | 1;
is_favorite?: 0 | 1;
is_hidden_from_feed?: 0 | 1;
common_count?: number;
occupation?: {
id?: number;
name?: string;
type?: "work" | "school" | "university";
crop: {
x: number;
y: number;
x2: number;
y2: number;
};
career?: {
group_id?: number;
company?: string;
country_id?: number;
city_id?: number;
city_name?: string;
from?: number;
until?: number;
position?: string;
rect: {
x: number;
y: number;
x2: number;
y2: number;
};
military?: {
country_id: number;
from?: number;
unit: string;
unit_id: number;
until?: number;
};
education?: {
university?: number;
university_name?: string;
faculty?: number;
faculty_name?: string;
graduation?: number;
};
home_town?: string;
relation?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
relation_partner?: {
deactivated?: string;
first_name: string;
hidden?: number;
id: number;
last_name: string;
can_access_closed?: boolean;
is_closed?: boolean;
};
personal?: {
alcohol?: 1 | 2 | 3 | 4 | 5;
inspired_by?: string;
langs?: string[];
life_main?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
people_main?: 1 | 2 | 3 | 4 | 5 | 6;
political?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
religion?: string;
smoking?: 1 | 2 | 3 | 4 | 5;
};
universities?: Array<{
chair?: number;
chair_name?: string;
city?: number;
country?: number;
education_form?: string;
education_status?: string;
faculty?: number;
faculty_name?: string;
graduation?: number;
id?: number;
name?: string;
university_group_id?: number;
}>;
schools?: Array<{
city?: number;
class?: string;
country?: number;
id?: string;
name?: string;
type?: number;
type_str?: string;
year_from?: number;
year_graduated?: number;
year_to?: number;
speciality?: string;
}>;
relatives?: Array<{
id?: number;
name?: string;
type: "parent" | "child" | "grandparent" | "grandchild" | "sibling";
}>;
counters?: {
albums?: number;
videos?: number;
audios?: number;
photos?: number;
notes?: number;
friends?: number;
groups?: number;
online_friends?: number;
mutual_friends?: number;
user_videos?: number;
followers?: number;
pages?: number;
};
is_no_index?: 0 | 1;
};
followers_count?: number;
blacklisted?: 0 | 1;
blacklisted_by_me?: 0 | 1;
is_favorite?: 0 | 1;
is_hidden_from_feed?: 0 | 1;
common_count?: number;
occupation?: {
id?: number;
name?: string;
type?: "work" | "school" | "university";
};
career?: {
group_id?: number;
company?: string;
country_id?: number;
city_id?: number;
city_name?: string;
from?: number;
until?: number;
position?: string;
};
military?: {
country_id: number;
from?: number;
unit: string;
unit_id: number;
until?: number;
};
education?: {
university?: number;
university_name?: string;
faculty?: number;
faculty_name?: string;
graduation?: number;
};
home_town?: string;
relation?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
relation_partner?: {
deactivated?: string;
first_name: string;
hidden?: number;
id: number;
last_name: string;
can_access_closed?: boolean;
is_closed?: boolean;
};
personal?: {
alcohol?: 1 | 2 | 3 | 4 | 5;
inspired_by?: string;
langs?: string[];
life_main?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
people_main?: 1 | 2 | 3 | 4 | 5 | 6;
political?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
religion?: string;
smoking?: 1 | 2 | 3 | 4 | 5;
};
universities?: Array<{
chair?: number;
chair_name?: string;
city?: number;
country?: number;
education_form?: string;
education_status?: string;
faculty?: number;
faculty_name?: string;
graduation?: number;
id?: number;
name?: string;
university_group_id?: number;
}>;
schools?: Array<{
city?: number;
class?: string;
country?: number;
id?: string;
name?: string;
type?: number;
type_str?: string;
year_from?: number;
year_graduated?: number;
year_to?: number;
speciality?: string;
}>;
relatives?: Array<{
id?: number;
name?: string;
type: "parent" | "child" | "grandparent" | "grandchild" | "sibling";
}>;
counters?: {
albums?: number;
videos?: number;
audios?: number;
photos?: number;
notes?: number;
friends?: number;
groups?: number;
online_friends?: number;
mutual_friends?: number;
user_videos?: number;
followers?: number;
pages?: number;
};
is_no_index?: 0 | 1;
email?: string;
}

@@ -267,0 +267,0 @@ /**

@@ -79,9 +79,20 @@ /**

token: `https://oauth.vk.com/access_token?v=${apiVersion}`,
userinfo: `https://api.vk.com/method/users.get?fields=photo_100&v=${apiVersion}`,
profile(result) {
const profile = result.response?.[0] ?? {};
userinfo: {
url: `https://api.vk.com/method/users.get?fields=photo_100&v=${apiVersion}`,
async request({ tokens, provider }) {
const profile = await fetch(provider.userinfo?.url, {
headers: {
Authorization: `Bearer ${tokens.access_token}`,
"User-Agent": "authjs",
},
}).then(async (res) => await res.json());
profile.response[0].email = tokens.email ? tokens.email : null;
return profile.response[0];
},
},
profile(profile) {
return {
id: profile.id,
name: [profile.first_name, profile.last_name].filter(Boolean).join(" "),
email: null,
email: profile.email ?? null,
image: profile.photo_100,

@@ -88,0 +99,0 @@ };

/**
* <div style={{backgroundColor: "#ffcc00", display: "flex", justifyContent: "space-between", color: "#000", padding: 16}}>
* <span>Built-in <b>Yandex</b> integration.</span>
* <a href="https://github.com">
* <a href="https://yandex.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/yandex.svg" height="48" width="48"/>

@@ -6,0 +6,0 @@ * </a>

/**
* <div style={{backgroundColor: "#ffcc00", display: "flex", justifyContent: "space-between", color: "#000", padding: 16}}>
* <span>Built-in <b>Yandex</b> integration.</span>
* <a href="https://github.com">
* <a href="https://yandex.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/yandex.svg" height="48" width="48"/>

@@ -6,0 +6,0 @@ * </a>

@@ -171,2 +171,3 @@ /**

const page = (isAuthError && error.kind) || "error"
// TODO: Filter out some error types from being sent to the client
const params = new URLSearchParams({ error: type })

@@ -173,0 +174,0 @@ const path =

@@ -8,4 +8,4 @@ /**

*
* The JWT issued by Auth.js is _encrypted by default_, using the _A256GCM_ algorithm ({@link https://www.rfc-editor.org/rfc/rfc7516 JWE}).
* It uses the `AUTH_SECRET` environment variable to derive a sufficient encryption key.
* The JWT issued by Auth.js is _encrypted by default_, using the _A256CBC-HS512_ algorithm ({@link https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5 JWE}).
* It uses the `AUTH_SECRET` environment variable or the passed `secret` propery to derive a suitable encryption key.
*

@@ -52,9 +52,12 @@ * :::info Note

/** Issues a JWT. By default, the JWT is encrypted using "A256GCM". */
const alg = "dir"
const enc = "A256CBC-HS512"
/** Issues a JWT. By default, the JWT is encrypted using "A256CBC-HS512". */
export async function encode<Payload = JWT>(params: JWTEncodeParams<Payload>) {
const { token = {}, secret, maxAge = DEFAULT_MAX_AGE, salt } = params
const encryptionSecret = await getDerivedEncryptionKey(secret, salt)
const encryptionSecret = await getDerivedEncryptionKey(enc, secret, salt)
// @ts-expect-error `jose` allows any object as payload.
return await new EncryptJWT(token)
.setProtectedHeader({ alg: "dir", enc: "A256GCM" })
.setProtectedHeader({ alg, enc })
.setIssuedAt()

@@ -72,6 +75,11 @@ .setExpirationTime(now() + maxAge)

if (!token) return null
const encryptionSecret = await getDerivedEncryptionKey(secret, salt)
const { payload } = await jwtDecrypt(token, encryptionSecret, {
clockTolerance: 15,
})
const { payload } = await jwtDecrypt(
token,
async ({ enc }) => await getDerivedEncryptionKey(enc, secret, salt),
{
clockTolerance: 15,
keyManagementAlgorithms: [alg],
contentEncryptionAlgorithms: [enc, "A256GCM"]
}
)
return payload as Payload

@@ -158,5 +166,17 @@ }

async function getDerivedEncryptionKey(
enc: string,
keyMaterial: Parameters<typeof hkdf>[1],
salt: Parameters<typeof hkdf>[2]
) {
let length: number
switch (enc) {
case "A256CBC-HS512":
length = 64
break
case "A256GCM":
length = 32
break
default:
throw new Error("Unsupported JWT Content Encryption Algorithm")
}
return await hkdf(

@@ -167,3 +187,3 @@ "sha256",

`Auth.js Generated Encryption Key (${salt})`,
32
length
)

@@ -170,0 +190,0 @@ }

@@ -200,3 +200,3 @@ import * as checks from "./checks.js"

...userFromProfile,
id: userFromProfile.id?.toString() ?? crypto.randomUUID(),
id: crypto.randomUUID(),
email: userFromProfile.email?.toLowerCase(),

@@ -203,0 +203,0 @@ } satisfies User

@@ -126,8 +126,6 @@ import { JWTSessionError, SessionTokenError } from "../../errors.js"

const sessionPayload = await callbacks.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 },
expires: session.expires.toISOString(),
},
// TODO: user already passed below,
// remove from session object in https://github.com/nextauthjs/next-auth/pull/9702
// @ts-expect-error
session: { ...session, user },
user,

@@ -134,0 +132,0 @@ newSession,

@@ -54,2 +54,4 @@ import { createHash, randomString, toRequest } from "../../utils/web.js"

const baseUrl = new URL(options.basePath, options.url.origin)
const sendRequest = provider.sendVerificationRequest({

@@ -59,3 +61,3 @@ identifier: email,

expires,
url: `${url}/callback/${provider.id}?${new URLSearchParams({
url: `${baseUrl}/callback/${provider.id}?${new URLSearchParams({
callbackUrl,

@@ -79,3 +81,3 @@ token,

return {
redirect: `${url}/verify-request?${new URLSearchParams({
redirect: `${baseUrl}/verify-request?${new URLSearchParams({
provider: provider.id,

@@ -82,0 +84,0 @@ type: provider.type,

@@ -44,3 +44,10 @@ import * as jwt from "../jwt.js"

session({ session }) {
return session
return {
user: {
name: session.user?.name,
email: session.user?.email,
image: session.user?.image,
},
expires: session.expires?.toISOString?.() ?? session.expires,
}
},

@@ -146,3 +153,3 @@ jwt({ token }) {

...authOptions.experimental,
}
},
}

@@ -149,0 +156,0 @@

@@ -1,2 +0,1 @@

import { OAuthProfileParseError } from "../../errors.js"
import { merge } from "./merge.js"

@@ -90,11 +89,10 @@

* Returns basic user profile from the userinfo response/`id_token` claims.
* An `id` is generated internally (using `crypto.randomUUID()`) and will override `id` if provided.
* The result if this function is user to create the `User` in the database.
* @see https://authjs.dev/reference/core/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
* @see https://openid.net/specs/openid-connect-core-1_0.html#
*/
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,

@@ -101,0 +99,0 @@ email: profile.email,

@@ -134,5 +134,5 @@ import { parse as parseCookie, serialize } from "cookie"

} {
const a = pathname.split(base)
const a = pathname.match(new RegExp(`^${base}(.+)`))
if (a.length !== 2 || a[0] !== "")
if (a === null)
throw new UnknownAction(`Cannot parse action at ${pathname}`)

@@ -139,0 +139,0 @@

@@ -20,2 +20,4 @@ /**

export type BattleNetIssuer =
| "https://oauth.battle.net"
| "https://oauth.battlenet.com.cn"
| "https://www.battlenet.com.cn/oauth"

@@ -47,2 +49,4 @@ | `https://${"us" | "eu" | "kr" | "tw"}.battle.net/oauth`

* type BattleNetIssuer =
* | "https://oauth.battle.net"
* | "https://oauth.battlenet.com.cn"
* | "https://www.battlenet.com.cn/oauth"

@@ -49,0 +53,0 @@ * | "https://us.battle.net/oauth"

@@ -22,4 +22,6 @@ /**

username: string
/** the user's 4-digit discord-tag */
/** the user's Discord-tag */
discriminator: string
/** the user's display name, if it is set */
global_name: string | null
/**

@@ -149,3 +151,6 @@ * the user's avatar hash:

if (profile.avatar === null) {
const defaultAvatarNumber = parseInt(profile.discriminator) % 5
const defaultAvatarNumber =
profile.discriminator === "0"
? Number(BigInt(profile.id) >> BigInt(22)) % 6
: parseInt(profile.discriminator) % 5
profile.image_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNumber}.png`

@@ -158,3 +163,3 @@ } else {

id: profile.id,
name: profile.username,
name: profile.global_name ?? profile.username,
email: profile.email,

@@ -161,0 +166,0 @@ image: profile.image_url,

import type { CommonProviderOptions } from "./index.js"
import type { Awaitable, Theme } from "../types.js"
import { Transport, TransportOptions, createTransport } from "nodemailer"
import * as JSONTransport from "nodemailer/lib/json-transport/index.js"
import * as SendmailTransport from "nodemailer/lib/sendmail-transport/index.js"
import * as SESTransport from "nodemailer/lib/ses-transport/index.js"
import * as SMTPTransport from "nodemailer/lib/smtp-transport/index.js"
import * as SMTPPool from "nodemailer/lib/smtp-pool/index.js"
import * as StreamTransport from "nodemailer/lib/stream-transport/index.js"
// TODO: Kepts for backwards compatibility
// Remove this import and encourage users
// to import it from @auth/core/providers/nodemailer directly
import Nodemailer from "./nodemailer.js"
import type { NodemailerConfig, NodemailerUserConfig } from "./nodemailer.js"
// TODO: Make use of https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html for the string
type AllTransportOptions =
| string
| SMTPTransport
| SMTPTransport.Options
| SMTPPool
| SMTPPool.Options
| SendmailTransport
| SendmailTransport.Options
| StreamTransport
| StreamTransport.Options
| JSONTransport
| JSONTransport.Options
| SESTransport
| SESTransport.Options
| Transport<any>
| TransportOptions
export interface SendVerificationRequestParams {
identifier: string
url: string
expires: Date
provider: EmailConfig
token: string
theme: Theme
request: Request
}
/**
* The Email Provider needs to be configured with an e-mail client.
* By default, it uses `nodemailer`, which you have to install if this
* provider is present.
* @deprecated
*
* You can use a other services as well, like:
* - [Postmark](https://postmarkapp.com)
* - [Mailgun](https://www.mailgun.com)
* - [SendGrid](https://sendgrid.com)
* - etc.
* Import this provider from the `providers/nodemailer` submodule instead of `providers/email`.
*
* [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
* To log in with nodemailer, change `signIn("email")` to `signIn("nodemailer")`
*/
export interface EmailUserConfig extends Record<string, unknown> {
server?: AllTransportOptions
type?: "email"
/** @default `"Auth.js <no-reply@authjs.dev>"` */
from?: string
/**
* How long until the e-mail can be used to log the user in,
* in seconds. Defaults to 1 day
*
* @default 86400
*/
maxAge?: number
/** [Documentation](https://authjs.dev/guides/providers/email#customizing-emails) */
sendVerificationRequest?: (
params: SendVerificationRequestParams
) => Awaitable<void>
/**
* By default, we are generating a random verification token.
* You can make it predictable or modify it as you like with this method.
*
* @example
* ```ts
* Providers.Email({
* async generateVerificationToken() {
* return "ABC123"
* }
* })
* ```
* [Documentation](https://authjs.dev/guides/providers/email#customizing-the-verification-token)
*/
generateVerificationToken?: () => Awaitable<string>
/** If defined, it is used to hash the verification token when saving to the database . */
secret?: string
/**
* Normalizes the user input before sending the verification request.
*
* ⚠️ Always make sure this method returns a single email address.
*
* @note Technically, the part of the email address local mailbox element
* (everything before the `@` symbol) should be treated as 'case sensitive'
* according to RFC 2821, but in practice this causes more problems than
* it solves, e.g.: when looking up users by e-mail from databases.
* By default, we treat email addresses as all lower case,
* but you can override this function to change this behavior.
*
* [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
export default function Email(config: NodemailerUserConfig): NodemailerConfig {
return {
...Nodemailer(config),
id: "email",
name: "Email",
}
}
// TODO: Rename to Token provider
// when started working on https://github.com/nextauthjs/next-auth/discussions/1465
export type EmailProviderType = "email"
export interface EmailConfig extends CommonProviderOptions {
// defaults
id: "email"
type: "email"
name: "Email"
server: AllTransportOptions
id: string
type: EmailProviderType
name: string
from: string
maxAge: number
sendVerificationRequest: (
params: SendVerificationRequestParams
) => Awaitable<void>
/**
* This is copied into EmailConfig in parseProviders() don't use elsewhere
*/
options: EmailUserConfig
// user options
// TODO figure out a better way than copying from EmailUserConfig
sendVerificationRequest: (params: {
identifier: string
url: string
expires: Date
provider: EmailConfig
token: string
theme: Theme
request: Request
}) => Awaitable<void>
/** Used to hash the verification token. */
secret?: string
/** Used with HTTP-based email providers */
apiKey?: string
generateVerificationToken?: () => Awaitable<string>
normalizeIdentifier?: (identifier: string) => string
options: EmailUserConfig
}
// TODO: Rename to Token provider
// when started working on https://github.com/nextauthjs/next-auth/discussions/1465
export type EmailProviderType = "email"
export type EmailUserConfig = Omit<Partial<EmailConfig>, "options" | "type">
/**
* ## 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/core/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: EmailUserConfig): EmailConfig {
return {
id: "email",
type: "email",
name: "Email",
server: { host: "localhost", port: 25, auth: { user: "", pass: "" } },
from: "Auth.js <no-reply@authjs.dev>",
maxAge: 24 * 60 * 60,
async sendVerificationRequest(params) {
const { identifier, url, provider, theme } = params
const { host } = new URL(url)
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 (${failed.join(", ")}) could not be sent`)
}
},
options: config,
}
}
/**
* Email HTML body

@@ -399,3 +63,3 @@ * Insert invisible space into domains from being turned into a hyperlink by email

*/
function html(params: { url: string; host: string; theme: Theme }) {
export function html(params: { url: string; host: string; theme: Theme }) {
const { url, host, theme } = params

@@ -453,4 +117,4 @@

/** Email Text body (fallback for email clients that don't render HTML, e.g. feature phones) */
function text({ url, host }: { url: string; host: string }) {
export function text({ url, host }: { url: string; host: string }) {
return `Sign in to ${host}\n${url}\n\n`
}

@@ -49,5 +49,7 @@

| "netlify"
| "nodemailer"
| "notion"
| "okta"
| "onelogin"
| "ory-hydra"
| "osso"

@@ -60,3 +62,5 @@ | "osu"

| "reddit"
| "resend"
| "salesforce"
| "sendgrid"
| "slack"

@@ -63,0 +67,0 @@ | "spotify"

@@ -13,281 +13,281 @@ /**

/** https://dev.vk.com/reference/objects/user */
export interface VkProfile {
// https://dev.vk.com/reference/objects/user
response: Array<{
id: number
first_name: string
last_name: string
photo_100: string
can_access_closed: boolean
is_closed: boolean
deactivated?: string
sex?: 0 | 1 | 2
screen_name?: string
photo_50?: string
online?: 0 | 1
online_mobile?: 0 | 1
online_app?: number
verified?: 0 | 1
trending?: 0 | 1
friend_status?: 0 | 1 | 2 | 3
first_name_nom?: string
first_name_gen?: string
first_name_dat?: string
first_name_acc?: string
first_name_ins?: string
first_name_abl?: string
last_name_nom?: string
last_name_gen?: string
last_name_dat?: string
last_name_acc?: string
last_name_ins?: string
last_name_abl?: string
nickname?: string
maiden_name?: string
domain?: string
bdate?: string
city?: {
id: number
first_name: string
last_name: string
photo_100: string
can_access_closed: boolean
is_closed: boolean
deactivated?: string
sex?: 0 | 1 | 2
screen_name?: string
photo_50?: string
online?: 0 | 1
online_mobile?: 0 | 1
online_app?: number
verified?: 0 | 1
trending?: 0 | 1
friend_status?: 0 | 1 | 2 | 3
first_name_nom?: string
first_name_gen?: string
first_name_dat?: string
first_name_acc?: string
first_name_ins?: string
first_name_abl?: string
last_name_nom?: string
last_name_gen?: string
last_name_dat?: string
last_name_acc?: string
last_name_ins?: string
last_name_abl?: string
nickname?: string
maiden_name?: string
domain?: string
bdate?: string
city?: {
id: number
title: string
}
country?: {
id: number
title: string
}
timezone?: number
photo_200?: string
photo_max?: string
photo_200_orig?: string
photo_400_orig?: string
photo_max_orig?: string
photo_id?: string
has_photo?: 0 | 1
has_mobile?: 0 | 1
is_friend?: 0 | 1
can_post?: 0 | 1
can_see_all_posts?: 0 | 1
can_see_audio?: 0 | 1
connections?: {
facebook?: string
skype?: string
twitter?: string
livejournal?: string
instagram?: string
}
photo_400?: string
wall_default?: "owner" | "all"
interests?: string
books?: string
tv?: string
quotes?: string
about?: string
games?: string
movies?: string
activities?: string
music?: string
can_write_private_message?: 0 | 1
can_send_friend_request?: 0 | 1
contacts?: {
mobile_phone?: string
home_phone?: string
}
site?: string
status_audio?: {
title: string
}
country?: {
id: number
title: string
}
timezone?: number
photo_200?: string
photo_max?: string
photo_200_orig?: string
photo_400_orig?: string
photo_max_orig?: string
photo_id?: string
has_photo?: 0 | 1
has_mobile?: 0 | 1
is_friend?: 0 | 1
can_post?: 0 | 1
can_see_all_posts?: 0 | 1
can_see_audio?: 0 | 1
connections?: {
facebook?: string
skype?: string
twitter?: string
livejournal?: string
instagram?: string
}
photo_400?: string
wall_default?: "owner" | "all"
interests?: string
books?: string
tv?: string
quotes?: string
about?: string
games?: string
movies?: string
activities?: string
music?: string
can_write_private_message?: 0 | 1
can_send_friend_request?: 0 | 1
contacts?: {
mobile_phone?: string
home_phone?: string
}
site?: string
status_audio?: {
access_key?: string
artist: string
id: number
owner_id: number
title: string
url?: string
duration: number
date?: number
album_id?: number
genre_id?: number
performer?: string
}
status?: string
last_seen?: {
platform?: 1 | 2 | 3 | 4 | 5 | 6 | 7
time?: number
}
exports?: {
facebook?: number
livejournal?: number
twitter?: number
instagram?: number
}
crop_photo?: {
photo: {
access_key?: string
artist: string
album_id: number
date: number
height?: number
id: number
owner_id: number
title: string
url?: string
duration: number
date?: number
album_id?: number
genre_id?: number
performer?: string
}
status?: string
last_seen?: {
platform?: 1 | 2 | 3 | 4 | 5 | 6 | 7
time?: number
}
exports?: {
facebook?: number
livejournal?: number
twitter?: number
instagram?: number
}
crop_photo?: {
photo: {
access_key?: string
album_id: number
date: number
images?: Array<{
height?: number
id: number
images?: Array<{
height?: number
type?: "s" | "m" | "x" | "l" | "o" | "p" | "q" | "r" | "y" | "z" | "w"
url?: string
width?: number
}>
lat?: number
long?: number
owner_id: number
photo_256?: string
can_comment?: 0 | 1
place?: string
post_id?: number
sizes?: Array<{
height: number
url: string
src?: string
type:
| "s"
| "m"
| "x"
| "o"
| "p"
| "q"
| "r"
| "k"
| "l"
| "y"
| "z"
| "c"
| "w"
| "a"
| "b"
| "e"
| "i"
| "d"
| "j"
| "temp"
| "h"
| "g"
| "n"
| "f"
| "max"
width: number
}>
text?: string
user_id?: number
type?: "s" | "m" | "x" | "l" | "o" | "p" | "q" | "r" | "y" | "z" | "w"
url?: string
width?: number
has_tags: boolean
}
crop: {
x: number
y: number
x2: number
y2: number
}
rect: {
x: number
y: number
x2: number
y2: number
}
}>
lat?: number
long?: number
owner_id: number
photo_256?: string
can_comment?: 0 | 1
place?: string
post_id?: number
sizes?: Array<{
height: number
url: string
src?: string
type:
| "s"
| "m"
| "x"
| "o"
| "p"
| "q"
| "r"
| "k"
| "l"
| "y"
| "z"
| "c"
| "w"
| "a"
| "b"
| "e"
| "i"
| "d"
| "j"
| "temp"
| "h"
| "g"
| "n"
| "f"
| "max"
width: number
}>
text?: string
user_id?: number
width?: number
has_tags: boolean
}
followers_count?: number
blacklisted?: 0 | 1
blacklisted_by_me?: 0 | 1
is_favorite?: 0 | 1
is_hidden_from_feed?: 0 | 1
common_count?: number
occupation?: {
id?: number
name?: string
type?: "work" | "school" | "university"
crop: {
x: number
y: number
x2: number
y2: number
}
career?: {
group_id?: number
company?: string
country_id?: number
city_id?: number
city_name?: string
from?: number
until?: number
position?: string
rect: {
x: number
y: number
x2: number
y2: number
}
military?: {
country_id: number
from?: number
unit: string
unit_id: number
until?: number
}
education?: {
university?: number
university_name?: string
faculty?: number
faculty_name?: string
graduation?: number
}
home_town?: string
relation?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
relation_partner?: {
deactivated?: string
first_name: string
hidden?: number
id: number
last_name: string
can_access_closed?: boolean
is_closed?: boolean
}
personal?: {
alcohol?: 1 | 2 | 3 | 4 | 5
inspired_by?: string
langs?: string[]
life_main?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
people_main?: 1 | 2 | 3 | 4 | 5 | 6
political?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
religion?: string
smoking?: 1 | 2 | 3 | 4 | 5
}
universities?: Array<{
chair?: number
chair_name?: string
city?: number
country?: number
education_form?: string
education_status?: string
faculty?: number
faculty_name?: string
graduation?: number
id?: number
name?: string
university_group_id?: number
}>
schools?: Array<{
city?: number
class?: string
country?: number
id?: string
name?: string
type?: number
type_str?: string
year_from?: number
year_graduated?: number
year_to?: number
speciality?: string
}>
relatives?: Array<{
id?: number
name?: string
type: "parent" | "child" | "grandparent" | "grandchild" | "sibling"
}>
counters?: {
albums?: number
videos?: number
audios?: number
photos?: number
notes?: number
friends?: number
groups?: number
online_friends?: number
mutual_friends?: number
user_videos?: number
followers?: number
pages?: number
}
is_no_index?: 0 | 1
}
followers_count?: number
blacklisted?: 0 | 1
blacklisted_by_me?: 0 | 1
is_favorite?: 0 | 1
is_hidden_from_feed?: 0 | 1
common_count?: number
occupation?: {
id?: number
name?: string
type?: "work" | "school" | "university"
}
career?: {
group_id?: number
company?: string
country_id?: number
city_id?: number
city_name?: string
from?: number
until?: number
position?: string
}
military?: {
country_id: number
from?: number
unit: string
unit_id: number
until?: number
}
education?: {
university?: number
university_name?: string
faculty?: number
faculty_name?: string
graduation?: number
}
home_town?: string
relation?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
relation_partner?: {
deactivated?: string
first_name: string
hidden?: number
id: number
last_name: string
can_access_closed?: boolean
is_closed?: boolean
}
personal?: {
alcohol?: 1 | 2 | 3 | 4 | 5
inspired_by?: string
langs?: string[]
life_main?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
people_main?: 1 | 2 | 3 | 4 | 5 | 6
political?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
religion?: string
smoking?: 1 | 2 | 3 | 4 | 5
}
universities?: Array<{
chair?: number
chair_name?: string
city?: number
country?: number
education_form?: string
education_status?: string
faculty?: number
faculty_name?: string
graduation?: number
id?: number
name?: string
university_group_id?: number
}>
schools?: Array<{
city?: number
class?: string
country?: number
id?: string
name?: string
type?: number
type_str?: string
year_from?: number
year_graduated?: number
year_to?: number
speciality?: string
}>
relatives?: Array<{
id?: number
name?: string
type: "parent" | "child" | "grandparent" | "grandchild" | "sibling"
}>
counters?: {
albums?: number
videos?: number
audios?: number
photos?: number
notes?: number
friends?: number
groups?: number
online_friends?: number
mutual_friends?: number
user_videos?: number
followers?: number
pages?: number
}
is_no_index?: 0 | 1
// Expand from token https://dev.vk.com/en/reference/access-rights?ref=old_portal
email?: string
}

@@ -376,9 +376,22 @@

token: `https://oauth.vk.com/access_token?v=${apiVersion}`,
userinfo: `https://api.vk.com/method/users.get?fields=photo_100&v=${apiVersion}`,
profile(result: P) {
const profile = result.response?.[0] ?? {}
userinfo: {
url: `https://api.vk.com/method/users.get?fields=photo_100&v=${apiVersion}`,
async request({ tokens, provider }) {
const profile = await fetch(provider.userinfo?.url as URL, {
headers: {
Authorization: `Bearer ${tokens.access_token}`,
"User-Agent": "authjs",
},
}).then(async (res) => await res.json())
profile.response[0].email = tokens.email ? tokens.email : null;
return profile.response[0];
},
},
profile(profile: P) {
return {
id: profile.id,
name: [profile.first_name, profile.last_name].filter(Boolean).join(" "),
email: null,
email: profile.email ?? null,
image: profile.photo_100,

@@ -385,0 +398,0 @@ }

/**
* <div style={{backgroundColor: "#ffcc00", display: "flex", justifyContent: "space-between", color: "#000", padding: 16}}>
* <span>Built-in <b>Yandex</b> integration.</span>
* <a href="https://github.com">
* <a href="https://yandex.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/yandex.svg" height="48" width="48"/>

@@ -6,0 +6,0 @@ * </a>

@@ -63,3 +63,3 @@ /**

} from "oauth4webapi"
import type { Adapter, AdapterUser } from "./adapters.js"
import type { Adapter, AdapterSession, AdapterUser } from "./adapters.js"
import { AuthConfig } from "./index.js"

@@ -252,14 +252,11 @@ import type { JWT, JWTOptions } from "./jwt.js"

session: (
params: (
| {
session: Session
/** Available when {@link AuthConfig.session} is set to `strategy: "database"`. */
user: AdapterUser
}
| {
session: Session
/** Available when {@link AuthConfig.session} is set to `strategy: "jwt"` */
token: JWT
}
) & {
params: ({
session: { user: AdapterUser } & AdapterSession
/** Available when {@link AuthConfig.session} is set to `strategy: "database"`. */
user: AdapterUser
} & {
session: Session
/** Available when {@link AuthConfig.session} is set to `strategy: "jwt"` */
token: JWT
}) & {
/**

@@ -448,11 +445,3 @@ * Available when using {@link AuthConfig.session} `strategy: "database"` and an update is triggered for the session.

/**
* Returned by `useSession`, `getSession`, returned by the `session` callback
* and also the shape received as a prop on the `SessionProvider` React Context
*
* [`useSession`](https://authjs.devreference/nextjs/react/#usesession) |
* [`getSession`](https://authjs.dev/reference/utilities#getsession) |
* [`SessionProvider`](https://authjs.devreference/nextjs/react#sessionprovider) |
* [`session` callback](https://authjs.dev/guides/basics/callbacks#jwt-callback)
*/
/** The active session of the logged in user. */
export interface Session extends DefaultSession {}

@@ -464,7 +453,2 @@

* or the second parameter of the `session` callback, when using a database.
*
* [`signIn` callback](https://authjs.dev/guides/basics/callbacks#sign-in-callback) |
* [`session` callback](https://authjs.dev/guides/basics/callbacks#jwt-callback) |
* [`jwt` callback](https://authjs.dev/guides/basics/callbacks#jwt-callback) |
* [`profile` OAuth provider callback](https://authjs.dev/guides/providers/custom-provider)
*/

@@ -594,2 +578,3 @@ export interface User {

experimental: Record<string, boolean>
basePath: string
}

@@ -59,3 +59,3 @@ /**

import type { OAuth2TokenEndpointResponse, OpenIDTokenEndpointResponse } from "oauth4webapi";
import type { Adapter, AdapterUser } from "./adapters.js";
import type { Adapter, AdapterSession, AdapterUser } from "./adapters.js";
import type { JWT, JWTOptions } from "./jwt.js";

@@ -230,6 +230,8 @@ import type { Cookie } from "./lib/utils/cookie.js";

session: (params: ({
session: Session;
session: {
user: AdapterUser;
} & AdapterSession;
/** Available when {@link AuthConfig.session} is set to `strategy: "database"`. */
user: AdapterUser;
} | {
} & {
session: Session;

@@ -408,11 +410,3 @@ /** Available when {@link AuthConfig.session} is set to `strategy: "jwt"` */

}
/**
* Returned by `useSession`, `getSession`, returned by the `session` callback
* and also the shape received as a prop on the `SessionProvider` React Context
*
* [`useSession`](https://authjs.devreference/nextjs/react/#usesession) |
* [`getSession`](https://authjs.dev/reference/utilities#getsession) |
* [`SessionProvider`](https://authjs.devreference/nextjs/react#sessionprovider) |
* [`session` callback](https://authjs.dev/guides/basics/callbacks#jwt-callback)
*/
/** The active session of the logged in user. */
export interface Session extends DefaultSession {

@@ -424,7 +418,2 @@ }

* or the second parameter of the `session` callback, when using a database.
*
* [`signIn` callback](https://authjs.dev/guides/basics/callbacks#sign-in-callback) |
* [`session` callback](https://authjs.dev/guides/basics/callbacks#jwt-callback) |
* [`jwt` callback](https://authjs.dev/guides/basics/callbacks#jwt-callback) |
* [`profile` OAuth provider callback](https://authjs.dev/guides/providers/custom-provider)
*/

@@ -431,0 +420,0 @@ export interface User {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc