Compare commits

..

12 Commits

Author SHA1 Message Date
Balázs Orbán
a39b8cfdf4 fix(ts): correct credentials types in authorize callback 2023-02-08 16:12:19 +01:00
AlandSleman
cee1bddbd5 docs: Fix Getting started page (#6636)
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-02-07 15:08:29 +01:00
Rémi Robichet
e0ae913e5c docs: fix type import (#6638) 2023-02-07 15:06:48 +01:00
Thang Vu
1db27fcd07 chore(monorepo): cache docs/build 2023-02-06 10:03:32 +07:00
Thang Vu
95407df289 chore(monorepo): cache typedoc generated files 2023-02-06 09:54:36 +07:00
Balázs Orbán
22adc2eb3c docs: fix redirects 2023-02-06 00:04:44 +01:00
Balázs Orbán
725f976b39 docs: fix redirects 2023-02-06 00:03:28 +01:00
MatyiFKBT
28ae5d4639 fix(providers): update Foursquare API version (#6620) 2023-02-05 15:30:57 +01:00
GitHub Actions
0f4d43e9ad chore(release): bump package version(s) [skip ci] 2023-02-05 13:52:31 +00:00
Thang Vu
28583b8ab0 feat: drop crypto dependency, convert to ESM (#6603)
Co-authored-by: Balázs Orbán <info@balazsorban.com>

BREAKING CHANGE:
- This package now only ships ESM, as all maintained Node.js versions have native support
- Dropped the `crypto` Node.js import in favor of `uuid`. When `globalThis.crypto` is the default in the future, we can remove `uuid` again
2023-02-05 14:44:33 +01:00
Balázs Orbán
1e7538a955 fix: publish .d.ts.map, add experimental warning 2023-02-05 14:42:07 +01:00
Wyatt Ades
4258857e52 feat(adapters): move to firebase-admin in Firebase Adapter (#6225)
Co-authored-by: Balázs Orbán <info@balazsorban.com>
fixes https://github.com/nextauthjs/next-auth/issues/5049
closes https://github.com/nextauthjs/next-auth/pull/6230
closes https://github.com/nextauthjs/next-auth/pull/5449
closes https://github.com/nextauthjs/next-auth/pull/5055
fixes https://github.com/nextauthjs/next-auth/issues/4927

BREAKING CHANGE:
The adapter now expects `firebase-admin` instead of the `firebase` package and also supports either passing a config object or a firestore instance.
2023-02-05 14:41:20 +01:00
16 changed files with 160 additions and 113 deletions

View File

@@ -253,11 +253,13 @@ Once inserted and correct, Github will redirect the user to our app and Auth.js
<img src={nextAuthUserLoggedIn} />
Great! We have completed the whole E2E authentication flow setup so that users can login in our application through Github!
:::
:::info
You can create your own Sign In page instead of using the default one from Auth.js. You can learn how to do so in our dedicated guide for it.
You can create your own Sign In page instead of using the default one from Auth.js. You can learn how to do so in our [dedicated guide for it](/guides/basics/pages).
:::
## 4. Deploying to production
### Configuring different environments

View File

@@ -11,7 +11,7 @@ When using SSR, I recommend creating a `Protected` component that will trigger s
```tsx
// components/Protected.tsx
import { type Session } from "@auth/core";
import { type Session } from "@auth/core/types";
import { getSession } from "@auth/solid-start";
import { Component, Show } from "solid-js";
import { useRouteData } from "solid-start";

View File

@@ -73,7 +73,7 @@
"value": "sveltekit.authjs.dev"
}
],
"destination": "https://authjs.dev/reference/sveltekit/modules/main"
"destination": "https://authjs.dev/reference/sveltekit"
},
{
"source": "/",
@@ -93,7 +93,7 @@
"value": "errors.authjs.dev"
}
],
"destination": "https://authjs.dev/reference/core/modules/errors/:path*"
"destination": "https://authjs.dev/reference/core/errors/:path*"
},
{
"source": "/:path(.*)",
@@ -123,7 +123,7 @@
"value": "providers.authjs.dev"
}
],
"destination": "https://authjs.dev/reference/core/functions/providers_:path.default"
"destination": "https://authjs.dev/reference/core/providers_:path.default"
}
]
}

View File

@@ -1,7 +1,7 @@
{
"name": "@next-auth/dynamodb-adapter",
"repository": "https://github.com/nextauthjs/next-auth",
"version": "1.2.0",
"version": "3.0.0",
"description": "AWS DynamoDB adapter for next-auth.",
"keywords": [
"next-auth",
@@ -9,11 +9,18 @@
"oauth",
"dynamodb"
],
"type": "module",
"types": "./index.d.ts",
"homepage": "https://authjs.dev",
"bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues"
},
"main": "dist/index.js",
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js"
}
},
"private": false,
"publishConfig": {
"access": "public"
@@ -26,7 +33,10 @@
},
"files": [
"README.md",
"dist"
"index.js",
"index.d.ts",
"index.d.ts.map",
"src"
],
"author": "Pol Marnette",
"license": "ISC",
@@ -41,7 +51,11 @@
"@next-auth/adapter-test": "workspace:*",
"@next-auth/tsconfig": "workspace:*",
"@shelf/jest-dynamodb": "^2.1.0",
"@types/uuid": "^9.0.0",
"jest": "^27.4.3",
"next-auth": "workspace:*"
},
"dependencies": {
"uuid": "^9.0.0"
}
}

View File

@@ -1,4 +1,4 @@
import { randomBytes } from "crypto"
import { v4 as uuid } from "uuid"
import type {
BatchWriteCommandInput,
@@ -12,16 +12,12 @@ import type {
VerificationToken,
} from "next-auth/adapters"
import { format, generateUpdateExpression } from "./utils"
export { format, generateUpdateExpression }
export interface DynamoDBAdapterOptions {
tableName?: string,
partitionKey?: string,
sortKey?: string,
indexName?: string,
indexPartitionKey?: string,
tableName?: string
partitionKey?: string
sortKey?: string
indexName?: string
indexPartitionKey?: string
indexSortKey?: string
}
@@ -30,17 +26,17 @@ export function DynamoDBAdapter(
options?: DynamoDBAdapterOptions
): Adapter {
const TableName = options?.tableName ?? "next-auth"
const pk = options?.partitionKey ?? 'pk'
const sk = options?.sortKey ?? 'sk'
const IndexName = options?.indexName ?? 'GSI1'
const GSI1PK = options?.indexPartitionKey ?? 'GSI1PK'
const GSI1SK = options?.indexSortKey ?? 'GSI1SK'
const pk = options?.partitionKey ?? "pk"
const sk = options?.sortKey ?? "sk"
const IndexName = options?.indexName ?? "GSI1"
const GSI1PK = options?.indexPartitionKey ?? "GSI1PK"
const GSI1SK = options?.indexSortKey ?? "GSI1SK"
return {
async createUser(data) {
const user: AdapterUser = {
...(data as any),
id: randomBytes(16).toString("hex"),
id: uuid(),
}
await client.put({
@@ -50,8 +46,8 @@ export function DynamoDBAdapter(
[pk]: `USER#${user.id}`,
[sk]: `USER#${user.id}`,
type: "USER",
[GSI1PK]: `USER#${user.email as string}`,
[GSI1SK]: `USER#${user.email as string}`,
[GSI1PK]: `USER#${user.email}`,
[GSI1SK]: `USER#${user.email}`,
}),
})
@@ -165,7 +161,7 @@ export function DynamoDBAdapter(
async linkAccount(data) {
const item = {
...data,
id: randomBytes(16).toString("hex"),
id: uuid(),
[pk]: `USER#${data.userId}`,
[sk]: `ACCOUNT#${data.provider}#${data.providerAccountId}`,
[GSI1PK]: `ACCOUNT#${data.provider}`,
@@ -229,7 +225,7 @@ export function DynamoDBAdapter(
},
async createSession(data) {
const session = {
id: randomBytes(16).toString("hex"),
id: uuid(),
...data,
}
await client.put({
@@ -327,3 +323,73 @@ export function DynamoDBAdapter(
},
}
}
// https://github.com/honeinc/is-iso-date/blob/master/index.js
const isoDateRE =
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/
function isDate(value: any) {
return value && isoDateRE.test(value) && !isNaN(Date.parse(value))
}
const format = {
/** Takes a plain old JavaScript object and turns it into a Dynamodb object */
to(object: Record<string, any>) {
const newObject: Record<string, unknown> = {}
for (const key in object) {
const value = object[key]
if (value instanceof Date) {
// DynamoDB requires the TTL attribute be a UNIX timestamp (in secs).
if (key === "expires") newObject[key] = value.getTime() / 1000
else newObject[key] = value.toISOString()
} else newObject[key] = value
}
return newObject
},
/** Takes a Dynamo object and returns a plain old JavaScript object */
from<T = Record<string, unknown>>(object?: Record<string, any>): T | null {
if (!object) return null
const newObject: Record<string, unknown> = {}
for (const key in object) {
// Filter DynamoDB specific attributes so it doesn't get passed to core,
// to avoid revealing the type of database
if (["pk", "sk", "GSI1PK", "GSI1SK"].includes(key)) continue
const value = object[key]
if (isDate(value)) newObject[key] = new Date(value)
// hack to keep type property in account
else if (key === "type" && ["SESSION", "VT", "USER"].includes(value))
continue
// The expires property is stored as a UNIX timestamp in seconds, but
// JavaScript needs it in milliseconds, so multiply by 1000.
else if (key === "expires" && typeof value === "number")
newObject[key] = new Date(value * 1000)
else newObject[key] = value
}
return newObject as T
},
}
function generateUpdateExpression(object: Record<string, any>): {
UpdateExpression: string
ExpressionAttributeNames: Record<string, string>
ExpressionAttributeValues: Record<string, unknown>
} {
const formatedSession = format.to(object)
let UpdateExpression = "set"
const ExpressionAttributeNames: Record<string, string> = {}
const ExpressionAttributeValues: Record<string, unknown> = {}
for (const property in formatedSession) {
UpdateExpression += ` #${property} = :${property},`
ExpressionAttributeNames["#" + property] = property
ExpressionAttributeValues[":" + property] = formatedSession[property]
}
UpdateExpression = UpdateExpression.slice(0, -1)
return {
UpdateExpression,
ExpressionAttributeNames,
ExpressionAttributeValues,
}
}
export { format, generateUpdateExpression }

View File

@@ -1,67 +0,0 @@
// https://github.com/honeinc/is-iso-date/blob/master/index.js
const isoDateRE =
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/
function isDate(value: any) {
return value && isoDateRE.test(value) && !isNaN(Date.parse(value))
}
export const format = {
/** Takes a plain old JavaScript object and turns it into a Dynamodb object */
to(object: Record<string, any>) {
const newObject: Record<string, unknown> = {}
for (const key in object) {
const value = object[key]
if (value instanceof Date) {
// DynamoDB requires the TTL attribute be a UNIX timestamp (in secs).
if (key === "expires") newObject[key] = value.getTime() / 1000
else newObject[key] = value.toISOString()
} else newObject[key] = value
}
return newObject
},
/** Takes a Dynamo object and returns a plain old JavaScript object */
from<T = Record<string, unknown>>(object?: Record<string, any>): T | null {
if (!object) return null
const newObject: Record<string, unknown> = {}
for (const key in object) {
// Filter DynamoDB specific attributes so it doesn't get passed to core,
// to avoid revealing the type of database
if (["pk", "sk", "GSI1PK", "GSI1SK"].includes(key)) continue
const value = object[key]
if (isDate(value)) newObject[key] = new Date(value)
// hack to keep type property in account
else if (key === "type" && ["SESSION", "VT", "USER"].includes(value))
continue
// The expires property is stored as a UNIX timestamp in seconds, but
// JavaScript needs it in milliseconds, so multiply by 1000.
else if (key === "expires" && typeof value === "number")
newObject[key] = new Date(value * 1000)
else newObject[key] = value
}
return newObject as T
},
}
export function generateUpdateExpression(object: Record<string, any>): {
UpdateExpression: string
ExpressionAttributeNames: Record<string, string>
ExpressionAttributeValues: Record<string, unknown>
} {
const formatedSession = format.to(object)
let UpdateExpression = "set"
const ExpressionAttributeNames: Record<string, string> = {}
const ExpressionAttributeValues: Record<string, unknown> = {}
for (const property in formatedSession) {
UpdateExpression += ` #${property} = :${property},`
ExpressionAttributeNames["#" + property] = property
ExpressionAttributeValues[":" + property] = formatedSession[property]
}
UpdateExpression = UpdateExpression.slice(0, -1)
return {
UpdateExpression,
ExpressionAttributeNames,
ExpressionAttributeValues,
}
}

View File

@@ -1,4 +1,4 @@
import { format } from "../src/utils"
import { format } from "../src/"
describe("dynamodb utils.format", () => {
it("format.to() preserves non-Date non-expires properties", () => {

View File

@@ -2,7 +2,15 @@
"extends": "@next-auth/tsconfig/tsconfig.adapters.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
"outDir": ".",
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"skipDefaultLibCheck": true,
"strictNullChecks": true,
"stripInternal": true,
"declarationMap": true,
"declaration": true
},
"exclude": ["tests", "dist", "jest.config.js", "jest-dynamodb-config.js"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@next-auth/firebase-adapter",
"version": "1.0.3",
"version": "2.0.0",
"description": "Firebase adapter for next-auth.",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
@@ -52,4 +52,4 @@
"jest": "^29.3.1",
"next-auth": "workspace:*"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/core",
"version": "0.3.0",
"version": "0.4.0",
"description": "Authentication for the Web.",
"keywords": [
"authentication",
@@ -27,7 +27,7 @@
"types": "./index.d.ts",
"files": [
"*.js",
"*.d.ts",
"*.d.ts*",
"lib",
"providers",
"src"
@@ -93,4 +93,4 @@
"postcss": "8.4.19",
"postcss-nested": "6.0.0"
}
}
}

View File

@@ -264,7 +264,7 @@ export async function callback(params: {
// Callback URL is already verified at this point, so safe to use if specified
return { redirect: callbackUrl, cookies }
} else if (provider.type === "credentials" && method === "POST") {
const credentials = body
const credentials = body ?? {}
// TODO: Forward the original request as is, instead of reconstructing it
Object.entries(query ?? {}).forEach(([k, v]) =>

View File

@@ -40,8 +40,16 @@ export interface CredentialsConfig<
* //...
*/
authorize: (
/** See {@link CredentialInput} */
credentials: Record<keyof CredentialsInputs, string> | undefined,
/**
* The available keys are determined by {@link CredentialInput}.
*
* @note The existence/correctness of a field cannot be guaranteed at runtime,
* so you should always validate the input before using it.
*
* You can add basic validation depending on your use case,
* or you can use a popular library like [Zod](https://zod.dev) for example.
*/
credentials: Partial<Record<keyof CredentialsInputs, unknown>>,
/** The original request is forward for convenience */
request: Request
) => Awaitable<User | null>

View File

@@ -1,6 +1,6 @@
/** @type {import(".").OAuthProvider} */
export default function Foursquare(options) {
const { apiVersion = "20210801" } = options
const { apiVersion = "20230131" } = options
return {
id: "foursquare",
name: "Foursquare",
@@ -15,7 +15,7 @@ export default function Foursquare(options) {
return fetch(url).then((res) => res.json())
},
},
profile({ response: { profile } }) {
profile({ response: { user: profile } }) {
return {
id: profile.id,
name: `${profile.firstName} ${profile.lastName}`,

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/sveltekit",
"version": "0.2.0",
"version": "0.2.1",
"description": "Authentication for SvelteKit.",
"keywords": [
"authentication",

17
pnpm-lock.yaml generated
View File

@@ -243,14 +243,19 @@ importers:
'@next-auth/adapter-test': workspace:*
'@next-auth/tsconfig': workspace:*
'@shelf/jest-dynamodb': ^2.1.0
'@types/uuid': ^9.0.0
jest: ^27.4.3
next-auth: workspace:*
uuid: ^9.0.0
dependencies:
uuid: 9.0.0
devDependencies:
'@aws-sdk/client-dynamodb': 3.113.0
'@aws-sdk/lib-dynamodb': 3.113.0_eb2z3hhrjl3qvyc6ecmpo2nhva
'@next-auth/adapter-test': link:../adapter-test
'@next-auth/tsconfig': link:../tsconfig
'@shelf/jest-dynamodb': 2.2.4_qsruu6yolbxs4rh6ixjhkibvwu
'@types/uuid': 9.0.0
jest: 27.5.1
next-auth: link:../next-auth
@@ -12750,6 +12755,10 @@ packages:
resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==}
dev: true
/@types/uuid/9.0.0:
resolution: {integrity: sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==}
dev: true
/@types/validator/13.7.3:
resolution: {integrity: sha512-DNviAE5OUcZ5s+XEQHRhERLg8fOp8gSgvyJ4aaFASx5wwaObm+PBwTIMXiOFm1QrSee5oYwEAYb7LMzX2O88gA==}
dev: true
@@ -13741,10 +13750,8 @@ packages:
indent-string: 4.0.0
dev: true
/ajv-formats/2.1.1_ajv@8.11.0:
/ajv-formats/2.1.1:
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
peerDependencies:
ajv: ^8.0.0
peerDependenciesMeta:
ajv:
optional: true
@@ -19535,7 +19542,7 @@ packages:
dependencies:
'@apidevtools/json-schema-ref-parser': 9.0.9
ajv: 8.11.0
ajv-formats: 2.1.1_ajv@8.11.0
ajv-formats: 2.1.1
body-parser: 1.20.0
content-type: 1.0.4
deep-freeze: 0.0.1
@@ -30975,7 +30982,7 @@ packages:
dependencies:
'@types/json-schema': 7.0.11
ajv: 8.11.0
ajv-formats: 2.1.1_ajv@8.11.0
ajv-formats: 2.1.1
ajv-keywords: 5.1.0_ajv@8.11.0
dev: true

View File

@@ -47,6 +47,15 @@
"docs#dev": {
"dependsOn": ["^build"],
"cache": false
},
"docs#build": {
"dependsOn": ["^build"],
"outputs": [
"build",
"docs/reference/core",
"docs/reference/sveltekit",
"docs/reference/adapter/**"
]
}
}
}