Compare commits

...

8 Commits

Author SHA1 Message Date
Balázs Orbán
a1e30507c2 feat(ts): support module augmentation (#1681)
* chore(ts): remove unused imports

* refactor(ts): clean up CallbackOptions

* docs(ts): explain Module Augmentation

* docs(ts): don't use @ in folder name "types"

* test(ts): make jwt params optional

* docs(ts): fix typo (TypeScript -> NextAuth.js)
2021-04-10 23:48:26 +02:00
Balázs Orbán
2c4fce3699 fix(build): fix release 2021-04-09 21:26:00 +02:00
Balázs Orbán
8fa71512d1 fix(built): typo in package.json 2021-04-09 21:20:41 +02:00
Balázs Orbán
d420eeff9d fix(ts): add .d.ts sub-module files to package.json
#1677 seemed to miss this
2021-04-09 21:10:43 +02:00
Lluis Agusti
0d863d38bc fix(ts): correctly export sub-module types (#1677)
* chore(types): build types script

Adds a script that moves the declaration files we have in `./types` to `./dist` relative to the files they intend to type.

This is the first step, we still need to change what we declare in `package.json`, add the script to the CI pipeline if we're happy with it and figure out how to type `next-auth/jwt`.

* refactor(lint): fix build-types script
2021-04-09 20:28:11 +02:00
Lluis Agusti
6f9f42a85b chore(ci): fix typo on types workflow 2021-04-07 17:05:48 +02:00
Lluis Agusti
2160be2a8a feat(ts): expose types from the package (#1665)
* chore(types): move existing types to the repo
* feat(ts): expose types from the main package
* chore(deps): bring back `react-dom` version range
* chore(ts): cleanup deps and comments
* chore(ci): run types tests on a separate workflow
2021-04-07 17:03:17 +02:00
Balázs Orbán
55eb066793 chore: add beta to release flow/GH actions 2021-04-04 22:08:25 +02:00
27 changed files with 28189 additions and 629 deletions

View File

@@ -6,10 +6,12 @@ on:
push:
branches:
- main
- beta
- next
pull_request:
branches:
- main
- beta
- next
jobs:

View File

@@ -13,7 +13,7 @@ name: "CodeQL"
on:
push:
branches: [ main, next ]
branches: [ main, beta, next ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]

View File

@@ -2,9 +2,10 @@ name: Integration Test
on:
push:
branches:
- main
- next
branches:
- main
- beta
- next
pull_request:
jobs:
@@ -17,7 +18,7 @@ jobs:
if: github.event.pull_request.head.repo.full_name == github.repository
# We use self-hosted runners as cloud based runnners (e.g. AWS, GPC)
# fail due to IP Address checks done by providers, which enforce
# fail due to IP Address checks done by providers, which enforce
# CAPTCHA checks on login request from cloud compute IP addresses to
# prevent abuse.
runs-on: self-hosted
@@ -45,7 +46,7 @@ jobs:
- run: npm test
# TODO Tests should exit out if env vars not set (currently hangs)
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
NEXTAUTH_TWITTER_ID: ${{secrets.NEXTAUTH_TWITTER_ID}}
NEXTAUTH_TWITTER_SECRET: ${{secrets.NEXTAUTH_TWITTER_SECRET}}
NEXTAUTH_TWITTER_USERNAME: ${{secrets.NEXTAUTH_TWITTER_USERNAME}}

View File

@@ -3,6 +3,7 @@ on:
push:
branches:
- 'main'
- 'beta'
- 'next'
- '3.x'
pull_request:

25
.github/workflows/types.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Types
on:
push:
branches:
- main
- beta
- next
pull_request:
branches:
- main
- beta
- next
jobs:
lint-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
- name: Install dependencies
uses: bahmutov/npm-install@v1
- name: Check types
run: npm run test:types

3
.npmignore Normal file
View File

@@ -0,0 +1,3 @@
./types/tests/
./types/tests/tsconfig.json
./types/tests/tslint.json

3
.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"semi": false
}

23
config/build-types.js Normal file
View File

@@ -0,0 +1,23 @@
const fs = require('fs')
const path = require('path')
const BUILD_TARGETS = [
'index.d.ts',
'client.d.ts',
'adapters.d.ts',
'providers.d.ts',
'jwt.d.ts',
'_next.d.ts',
'_utils.d.ts'
]
BUILD_TARGETS.forEach((target) => {
fs.copyFile(
path.resolve('types', target),
path.join(process.cwd(), target),
(err) => {
if (err) throw err
console.log(`[build-types] copying "${target}" to root folder`)
}
)
})

26891
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,10 @@
"author": "Iain Collins <me@iaincollins.com>",
"main": "index.js",
"scripts": {
"build": "npm run build:js && npm run build:css",
"build": "npm run build:js && npm run build:css && npm run build:types",
"build:js": "babel --config-file ./config/babel.config.json src --out-dir dist",
"build:css": "postcss --config config/postcss.config.js src/**/*.css --base src --dir dist && node config/wrap-css.js",
"build:types": "node ./config/build-types.js",
"dev": "next | npm run watch:css",
"watch": "npm run watch:js | npm run watch:css",
"watch:js": "babel --config-file ./config/babel.config.json --watch src --out-dir dist",
@@ -17,13 +18,14 @@
"test:app:start": "docker-compose -f test/docker/app.yml up -d",
"test:app:rebuild": "npm run build && docker-compose -f test/docker/app.yml up -d --build",
"test:app:stop": "docker-compose -f test/docker/app.yml down",
"test": "npm run test:app:rebuild && npm run test:integration && npm run test:app:stop",
"test": "npm run test:app:rebuild && npm run test:integration && npm run test:app:stop && npm run test:types",
"test:db": "npm run test:db:mysql && npm run test:db:postgres && npm run test:db:mongodb && npm run test:db:mssql",
"test:db:mysql": "node test/mysql.js",
"test:db:postgres": "node test/postgres.js",
"test:db:mongodb": "node test/mongodb.js",
"test:db:mssql": "node test/mssql.js",
"test:integration": "mocha test/integration",
"test:types": "dtslint types",
"db:start": "docker-compose -f test/docker/databases.yml up -d",
"db:stop": "docker-compose -f test/docker/databases.yml down",
"prepublishOnly": "npm run build",
@@ -35,10 +37,17 @@
"files": [
"dist",
"index.js",
"index.d.ts",
"providers.js",
"providers.d.ts",
"adapters.js",
"adapters.d.ts",
"client.js",
"jwt.js"
"client.d.ts",
"jwt.js",
"jwt.d.ts",
"_next.d.ts",
"_utils.d.ts"
],
"license": "ISC",
"dependencies": {
@@ -57,7 +66,7 @@
},
"peerDependencies": {
"react": "^16.13.1 || ^17",
"react-dom": "^16.13.1 || ^17"
"react-dom": "16.13.1 || ^17"
},
"peerOptionalDependencies": {
"mongodb": "^3.5.9",
@@ -81,6 +90,7 @@
"conventional-changelog-conventionalcommits": "4.4.0",
"cssnano": "^4.1.10",
"dotenv": "^8.2.0",
"dtslint": "^4.0.8",
"eslint": "^7.19.0",
"mocha": "^8.1.3",
"mongodb": "^3.5.9",
@@ -90,6 +100,7 @@
"pg": "^8.2.1",
"postcss-cli": "^7.1.1",
"postcss-nested": "^4.2.1",
"prettier": "^2.2.1",
"prisma": "^2.16.1",
"puppeteer": "^5.2.1",
"puppeteer-extra": "^3.1.15",
@@ -103,7 +114,8 @@
"project": "./tsconfig.json",
"ignore": [
"test/",
"next-env.d.ts"
"next-env.d.ts",
"types/"
],
"globals": [
"localStorage",

View File

@@ -2,6 +2,7 @@ module.exports = {
branches: [
'+([0-9])?(.{+([0-9]),x}).x',
'main',
{ name: 'beta', prerelease: true },
{ name: 'next', prerelease: true }
]
}

40
types/_next.d.ts vendored Normal file
View File

@@ -0,0 +1,40 @@
import { IncomingMessage, ServerResponse } from "http"
// ------------------------------------------------------
// Types from next@10,
// see: https://github.com/microsoft/dtslint/issues/297
// ------------------------------------------------------
export interface NextApiRequest extends IncomingMessage {
query: {
[key: string]: string | string[]
}
cookies: {
[key: string]: string
}
body: any
env: any
preview?: boolean
previewData?: any
}
export type Send<T> = (body: T) => void
export type NextApiResponse<T = any> = ServerResponse & {
send: Send<T>
json: Send<T>
status: (statusCode: number) => NextApiResponse<T>
redirect: ((url: string) => NextApiResponse<T>) &
((status: number, url: string) => NextApiResponse<T>)
setPreviewData: (
data: object | string,
options?: {
maxAge?: number
}
) => NextApiResponse<T>
clearPreviewData: () => NextApiResponse<T>
}
export type NextApiHandler<T = any> = (
req: NextApiRequest,
res: NextApiResponse<T>
) => void | Promise<void>

14
types/_utils.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
export type NonNullParams<T> = {
[K in keyof T]: T[K] extends Record<string, unknown>
? NonNullParams<T[K]>
: NonNullable<T[K]>
}
export type NullableParams<T> = {
[K in keyof T]: T[K] | undefined | null
}
export type WithAdditionalParams<T extends Record<string, any>> = T &
Record<string, unknown>
export type Awaitable<T> = T | PromiseLike<T>

242
types/adapters.d.ts vendored Normal file
View File

@@ -0,0 +1,242 @@
import { ConnectionOptions, EntitySchema } from "typeorm"
import { AppOptions, User } from "."
import { AppProvider } from "./providers"
export interface Profile {
id: string
name: string
email: string | null
image?: string | null
}
export interface Session {
userId: string | number | object
expires: Date
sessionToken: string
accessToken: string
}
export interface VerificationRequest {
identifier: string
token: string
expires: Date
}
export interface SendVerificationRequestParams {
identifier: string
url: string
token: string
baseUrl: string
provider: AppProvider
}
export type EmailAppProvider = AppProvider & {
sendVerificationRequest: (
params: SendVerificationRequestParams
) => Promise<void>
maxAge: number | undefined
}
export interface AdapterInstance<
TUser,
TProfile,
TSession,
TVerificationRequest
> {
createUser: (profile: TProfile) => Promise<TUser>
getUser: (id: string) => Promise<TUser | null>
getUserByEmail: (email: string) => Promise<TUser | null>
getUserByProviderAccountId: (
providerId: string,
providerAccountId: string
) => Promise<TUser | null>
updateUser: (user: TUser) => Promise<TUser>
linkAccount: (
userId: string,
providerId: string,
providerType: string,
providerAccountId: string,
refreshToken: string,
accessToken: string,
accessTokenExpires: number
) => Promise<void>
createSession: (user: TUser) => Promise<TSession>
getSession: (sessionToken: string) => Promise<TSession | null>
updateSession: (session: TSession, force?: boolean) => Promise<TSession>
deleteSession: (sessionToken: string) => Promise<void>
createVerificationRequest?: (
email: string,
url: string,
token: string,
secret: string,
provider: EmailAppProvider,
options: AppOptions
) => Promise<TVerificationRequest>
getVerificationRequest?: (
email: string,
verificationToken: string,
secret: string,
provider: AppProvider
) => Promise<TVerificationRequest | null>
deleteVerificationRequest?: (
email: string,
verificationToken: string,
secret: string,
provider: AppProvider
) => Promise<void>
}
interface Adapter<
TUser extends User = any,
TProfile extends Profile = any,
TSession extends Session = any,
TVerificationRequest extends VerificationRequest = any
> {
getAdapter: (
appOptions: AppOptions
) => Promise<AdapterInstance<TUser, TProfile, TSession, TVerificationRequest>>
}
type Schema<T = any> = EntitySchema<T>["options"]
interface Adapters {
Default: TypeORMAdapter["Adapter"]
TypeORM: TypeORMAdapter
Prisma: PrismaAdapter
}
/**
* TODO: fix auto-type schema
*/
interface TypeORMAdapter<
A extends TypeORMAccountModel = any,
U extends TypeORMUserModel = any,
S extends TypeORMSessionModel = any,
VR extends TypeORMVerificationRequestModel = any
> {
Adapter: (
typeOrmConfig: ConnectionOptions,
options?: {
models?: {
Account?: {
model: A
schema: Schema<A>
}
User?: {
model: U
schema: Schema<U>
}
Session?: {
model: S
schema: Schema<S>
}
VerificationRequest?: {
model: VR
schema: Schema<VR>
}
}
}
) => Adapter<U, Profile, S, VR>
Models: {
Account: {
model: TypeORMAccountModel
schema: Schema<TypeORMAccountModel>
}
User: {
model: TypeORMUserModel
schema: Schema<TypeORMUserModel>
}
Session: {
model: TypeORMSessionModel
schema: Schema<TypeORMSessionModel>
}
VerificationRequest: {
model: TypeORMVerificationRequestModel
schema: Schema<TypeORMVerificationRequestModel>
}
}
}
interface PrismaAdapter {
Adapter: (config: {
prisma: any
modelMapping?: {
User: string
Account: string
Session: string
VerificationRequest: string
}
}) => Adapter
}
declare const Adapters: Adapters
declare class TypeORMAccountModel {
compoundId: string
userId: number
providerType: string
providerId: string
providerAccountId: string
refreshToken?: string
accessToken?: string
accessTokenExpires?: Date
constructor(
userId: number,
providerId: string,
providerType: string,
providerAccountId: string,
refreshToken?: string,
accessToken?: string,
accessTokenExpires?: Date
)
}
declare class TypeORMUserModel implements User {
name?: string
email?: string
image?: string
emailVerified?: Date
constructor(
name?: string,
email?: string,
image?: string,
emailVerified?: Date
)
}
declare class TypeORMSessionModel implements Session {
userId: number
expires: Date
sessionToken: string
accessToken: string
constructor(
userId: number,
expires: Date,
sessionToken?: string,
accessToken?: string
)
}
declare class TypeORMVerificationRequestModel implements VerificationRequest {
identifier: string
token: string
expires: Date
constructor(identifier: string, token: string, expires: Date)
}
export default Adapters
export {
Adapter,
Adapters,
TypeORMAdapter,
TypeORMAccountModel,
TypeORMUserModel,
TypeORMSessionModel,
TypeORMVerificationRequestModel,
PrismaAdapter,
}

97
types/client.d.ts vendored Normal file
View File

@@ -0,0 +1,97 @@
import { FC } from "react"
import { IncomingMessage } from "http"
import { WithAdditionalParams } from "./_utils"
import { Session } from "."
import { AppProvider, DefaultProviders, Providers } from "./providers"
interface ContextProviderProps {
session: WithAdditionalParams<Session> | null | undefined
options?: SetOptionsParams
}
interface SetOptionsParams {
baseUrl?: string
basePath?: string
clientMaxAge?: number
keepAlive?: number
}
interface SignInResponse {
error: string | undefined
status: number
ok: boolean
url: string | null
}
type ContextProvider = FC<ContextProviderProps>
interface NextContext {
req?: IncomingMessage
ctx?: { req: IncomingMessage }
}
declare function useSession(): [Session | null | undefined, boolean]
declare function providers(): Promise<Record<
keyof DefaultProviders | string,
AppProvider
> | null>
declare const getProviders: typeof providers
declare function session(
context?: NextContext & {
triggerEvent?: boolean
}
): Promise<Session | null>
declare const getSession: typeof session
declare function csrfToken(context?: NextContext): Promise<string | null>
declare const getCsrfToken: typeof csrfToken
declare function signin(
provider: "credentials" | "email",
data?: Record<string, unknown> & {
callbackUrl?: string
redirect?: false
},
authorizationParams?:
| string
| string[][]
| Record<string, unknown>
| URLSearchParams
): Promise<SignInResponse>
declare function signin(
provider?: string,
data?: Record<string, unknown> & {
callbackUrl?: string
redirect?: boolean
},
authorizationParams?:
| string
| string[][]
| Record<string, unknown>
| URLSearchParams
): Promise<void>
declare const signIn: typeof signin
declare function signout(data?: {
callbackUrl?: string
redirect?: boolean
}): Promise<void>
declare const signOut: typeof signout
declare function options(options: SetOptionsParams): void
declare const setOptions: typeof options
declare const Provider: ContextProvider
export {
useSession,
session,
getSession,
providers,
getProviders,
csrfToken,
getCsrfToken,
signin,
signIn,
signout,
signOut,
options,
setOptions,
Provider,
}

173
types/index.d.ts vendored Normal file
View File

@@ -0,0 +1,173 @@
// Minimum TypeScript Version: 3.5
/// <reference types="node" />
import { ConnectionOptions } from "typeorm"
import { Adapter } from "./adapters"
import { JWTOptions, JWT } from "./jwt"
import { AppProvider, Providers } from "./providers"
import { NextApiRequest, NextApiResponse, NextApiHandler } from "./_next"
import { Awaitable, NonNullParams, WithAdditionalParams } from "./_utils"
export interface NextAuthOptions {
providers: Providers
database?: string | Record<string, any> | ConnectionOptions
secret?: string
session?: SessionOptions
jwt?: JWTOptions
pages?: PagesOptions
callbacks?: CallbacksOptions
debug?: boolean
adapter?: Adapter
events?: EventsOptions
useSecureCookies?: boolean
cookies?: CookiesOptions
logger?: LoggerInstance
theme?: "light" | "dark" | "auto"
}
export interface LoggerInstance {
warn: (code?: string, ...message: unknown[]) => void
error: (code?: string, ...message: unknown[]) => void
debug: (code?: string, ...message: unknown[]) => void
}
interface InternalOptions
extends Omit<
NextAuthOptions,
"providers" | "database" | "session" | "useSecureCookie"
> {
pkce: {
code_verifier?: string
code_challenge_method?: "S256"
}
provider?: string
baseUrl?: string
basePath?: string
action?:
| "providers"
| "session"
| "csrf"
| "signin"
| "signout"
| "callback"
| "verify-request"
| "error"
csrfToken?: string
}
export interface AppOptions
extends Omit<NextApiRequest, "cookies">,
NonNullParams<InternalOptions> {
providers: AppProvider[]
}
export interface Account extends Record<string, unknown> {
accessToken: string
idToken?: string
refreshToken?: string
access_token: string
expires_in?: number | null
refresh_token?: string
id_token?: string
id: string
provider: string
type: string
}
export interface Profile extends Record<string, unknown> {}
export interface CallbacksOptions<
P extends Record<string, unknown> = Profile,
A extends Record<string, unknown> = Account
> {
signIn?(user: User, account: A, profile: P): Awaitable<string | boolean>
redirect?(url: string, baseUrl: string): Awaitable<string>
session?(session: Session, userOrToken: JWT | User): Awaitable<Session>
jwt?(
token: JWT,
user?: User,
account?: A,
profile?: P,
isNewUser?: boolean
): Awaitable<JWT>
}
export interface CookieOption {
name: string
options: {
httpOnly: boolean
sameSite: true | "strict" | "lax" | "none"
path?: string
secure: boolean
maxAge?: number
domain?: string
}
}
export interface CookiesOptions {
sessionToken?: CookieOption
callbackUrl?: CookieOption
csrfToken?: CookieOption
pkceCodeVerifier?: CookieOption
}
export type EventType =
| "signIn"
| "signOut"
| "createUser"
| "updateUser"
| "linkAccount"
| "session"
| "error"
export type EventCallback = (message: any) => Promise<void>
export type EventsOptions = Partial<Record<EventType, EventCallback>>
export interface PagesOptions {
signIn?: string
signOut?: string
error?: string
verifyRequest?: string
newUser?: string | null
}
export interface Session {
user: WithAdditionalParams<User>
accessToken?: string
expires: string
}
export interface SessionOptions {
jwt?: boolean
maxAge?: number
updateAge?: number
}
export interface User {
name?: string | null
email?: string | null
image?: string | null
}
export interface NextAuthRequest extends NextApiRequest {
options: InternalOptions
}
export type NextAuthResponse = NextApiResponse
declare function NextAuthHandler(
req: NextApiRequest,
res: NextApiResponse,
options?: NextAuthOptions
): ReturnType<NextApiHandler>
declare function NextAuth(
req: NextApiRequest,
res: NextApiResponse,
options?: NextAuthOptions
): ReturnType<NextApiHandler>
declare function NextAuth(
options: NextAuthOptions
): ReturnType<typeof NextAuthHandler>
export { NextAuthHandler, NextAuth }
export default NextAuth

67
types/jwt.d.ts vendored Normal file
View File

@@ -0,0 +1,67 @@
import { JWT, JWE } from "jose"
import { NextApiRequest } from "./_next"
import { WithAdditionalParams } from "./_utils"
export interface JWT extends Record<string, unknown> {
name?: string | null
email?: string | null
picture?: string | null
}
export interface JWTEncodeParams {
token?: WithAdditionalParams<JWT>
maxAge?: number
secret: string | Buffer
signingKey?: string
signingOptions?: JWT.SignOptions
encryptionKey?: string
encryptionOptions?: object
encryption?: boolean
}
export interface JWTDecodeParams {
token?: string
maxAge?: number
secret: string | Buffer
signingKey?: string
verificationKey?: string
verificationOptions?: JWT.VerifyOptions<false>
encryptionKey?: string
decryptionKey?: string
decryptionOptions?: JWE.DecryptOptions<false>
encryption?: boolean
}
export interface JWTOptions {
secret?: string
maxAge?: number
encryption?: boolean
signingKey?: string
encryptionKey?: string
encode?: (options: JWTEncodeParams) => Promise<string>
decode?: (options: JWTDecodeParams) => Promise<WithAdditionalParams<JWT>>
}
declare function encode(args?: JWTEncodeParams): Promise<string>
declare function decode(
args?: JWTDecodeParams & { token: string }
): Promise<WithAdditionalParams<JWT>>
declare function getToken(
args?: {
req: NextApiRequest
secureCookie?: boolean
cookieName?: string
raw?: string
} & JWTDecodeParams
): Promise<WithAdditionalParams<JWT>>
declare function getToken(args?: {
req: NextApiRequest
secureCookie?: boolean
cookieName?: string
raw: true
}): Promise<string>
export { encode, decode, getToken }

435
types/providers.d.ts vendored Normal file
View File

@@ -0,0 +1,435 @@
import { User } from "."
import { JWT } from "./jwt"
import { NonNullParams, NullableParams, WithAdditionalParams } from "./_utils"
export interface Provider<
T extends string | undefined = undefined,
U = T extends string ? "oauth" : string
> {
id: T
name: string
type: U extends string ? U : "oauth" | "email" | "credentials"
version: string
scope: string
params: { grant_type: string }
accessTokenUrl: string
requestTokenUrl: string
authorizationUrl: string
profileUrl: string
profile: (
profile: Record<string, any>,
tokens: any
) => (User & { id: string }) | Promise<User & { id: string }>
clientId: string
clientSecret: string | Record<string, unknown>
idToken?: boolean
}
export interface AppProvider extends Pick<Provider, "id" | "name" | "type"> {
signinUrl: string
callbackUrl: string
}
export interface DefaultProviders {
Apple: Apple
Attlassian: Atlassian
Auth0: Auth0
AzureADB2C: AzureADB2C
Basecamp: Basecamp
BattleNet: BattleNet
Box: Box
Bungie: Bungie
Cognito: Cognito
Credentials: Credentials
Discord: Discord
Email: Email
EVEOnline: EVEOnline
Facebook: Facebook
FACEIT: FACEIT
Foursquare: Foursquare
FusionAuth: FusionAuth
GitHub: GitHub
GitLab: GitLab
Google: Google
IdentityServer4: IdentityServer4
Instagram: Instagram
Kakao: Kakao
LINE: LINE
LinkedIn: LinkedIn
MailRu: MailRu
Medium: Medium
Netlify: Netlify
Okta: Okta
Osso: Osso
Reddit: Reddit
Salesforce: Salesforce
Slack: Slack
Spotify: Spotify
Strava: Strava
Twitch: Twitch
Twitter: Twitter
VK: VK
Yandex: Yandex
Zoho: Zoho
}
export type Providers = Array<
Provider | ReturnType<DefaultProviders[keyof DefaultProviders]>
>
declare const Providers: DefaultProviders
export default Providers
/**
* Email
*/
type Email = (
options: ProviderEmailOptions
) => NonNullParams<ProviderEmailOptions> & { id: "email"; type: "email" }
interface VerificationRequestParams extends Provider {
identifier: string
url: string
baseUrl: string
token: string
provider: ProviderEmailOptions
}
interface ProviderEmailOptions {
name?: string
server?: string | ProviderEmailServer
from?: string
maxAge?: number
sendVerificationRequest?: (
options: VerificationRequestParams
) => Promise<void>
}
interface ProviderEmailServer {
host: string
port: number
auth: {
user: string
pass: string
}
}
/**
* Credentials
*/
type Credentials = (
options: ProviderCredentialsOptions
) => NonNullParams<ProviderCredentialsOptions> & {
id: "credentials"
type: "credentials"
}
interface ProviderCredentialsOptions {
id?: string
name: string
credentials: CredentialInput
authorize: (credentials: Record<string, string>) => Promise<User | null>
}
interface CredentialInput {
[key: string]: {
label?: string
type?: string
value?: string
placeholder?: string
}
}
type OptionsBase = {
[K in keyof Omit<Provider, "id">]?: Provider[K]
}
/**
* Provider options
* @link https://next-auth.js.org/configuration/providers#oauth-provider-options
*/
interface ProviderCommonOptions extends OptionsBase {
authorizationParams?: Record<string, string>
clientId: string
clientSecret: string
headers?: Record<string, any>
idToken?: boolean
name?: string
protection?: "pkce" | "state" | "both" | "none"
state?: boolean
}
/**
* Apple
*/
type Apple = (
options: ProviderAppleOptions
) => Provider<"apple"> & { protection: "none" }
interface ProviderAppleOptions
extends Omit<ProviderCommonOptions, "clientSecret"> {
name?: string
clientId: string
clientSecret: Record<"appleId" | "teamId" | "privateKey" | "keyId", string>
}
interface ProviderAppleSecret {
appleId: string
teamId: string
privateKey: string
keyId: string
}
/**
* Twitter
*/
type Twitter = (options: ProviderCommonOptions) => Provider<"twitter">
/**
* Facebook
*/
type Facebook = (options: ProviderCommonOptions) => Provider<"facebook">
/**
* GitHub
*/
type GitHub = (options: ProviderGitHubOptions) => Provider<"github">
interface ProviderGitHubOptions extends Omit<ProviderCommonOptions, "scope"> {
scope?: string
}
/**
* GitLab
*/
type GitLab = (options: ProviderCommonOptions) => Provider<"gitlab">
/**
* Slack
*/
type Slack = (options: ProviderCommonOptions) => Provider<"slack">
/**
* Google
*/
type Google = (options: ProviderGoogleOptions) => Provider<"google">
interface ProviderGoogleOptions extends ProviderCommonOptions {
authorizationUrl?: string
}
/**
* Auth0
*/
type Auth0 = (
options: ProviderAuth0Options
) => Provider<"auth0"> & { domain: string }
interface ProviderAuth0Options extends Omit<ProviderCommonOptions, "profile"> {
domain: string
profile?: (profile: Auth0Profile) => User & { id: string }
}
interface Auth0Profile {
sub: string
nickname: string
email: string
picture: string
}
/**
* IS4
*/
type IdentityServer4 = (
options: ProviderIS4Options
) => Provider<"identity-server4" | string> & { domain: string }
interface ProviderIS4Options extends Omit<ProviderCommonOptions, "id"> {
id: string
scope: string
domain: string
}
/**
* Discord
*/
type Discord = (options: ProviderCommonOptions) => Provider<"discord">
/**
* Twitch
*/
type Twitch = (options: ProviderCommonOptions) => Provider<"twitch">
/**
* Okta
*/
type Okta = (
options: ProviderOktaOptions
) => Provider<"okta"> & { domain: string }
interface ProviderOktaOptions extends ProviderCommonOptions {
domain: string
}
/**
* Battle.net
*/
type BattleNet = (
options: ProviderBattleNetOptions
) => Provider<"battlenet"> & { region: string }
interface ProviderBattleNetOptions extends ProviderCommonOptions {
region: string
}
/**
* Box
*/
type Box = (options: ProviderCommonOptions) => Provider<"box">
/**
* Cognito
*/
type Cognito = (
options: ProviderCognitoOptions
) => Provider<"cognito"> & { domain: string }
interface ProviderCognitoOptions extends ProviderCommonOptions {
domain: string
}
/**
* Yandex
*/
type Yandex = (options: ProviderCommonOptions) => Provider<"yandex">
/**
* LinkedIn
*/
type LinkedIn = (options: ProviderLinkedInOptions) => Provider<"linkedin">
interface ProviderLinkedInOptions extends ProviderCommonOptions {
scope?: string
}
/**
* Spotify
*/
type Spotify = (options: ProviderSpotifyOptions) => Provider<"spotify">
interface ProviderSpotifyOptions extends ProviderCommonOptions {
scope?: string
}
/**
* Basecamp
*/
type Basecamp = (options: ProviderCommonOptions) => Provider<"basecamp">
/**
* Reddit
*/
type Reddit = (options: ProviderCommonOptions) => Provider<"reddit">
/**
* Atlassian
*/
type Atlassian = (options: ProviderCommonOptions) => Provider<"atlassian">
/**
* AzureADB2C
*/
type AzureADB2C = (
options: ProviderAzureADB2COptions
) => Provider<"azure-ad-b2c">
interface ProviderAzureADB2COptions extends ProviderCommonOptions {
tenantId?: string
}
/**
* Bungie
*/
type Bungie = (options: ProviderCommonOptions) => Provider<"bungie">
/**
* EVEOnline
*/
type EVEOnline = (options: ProviderCommonOptions) => Provider<"eveonline">
/**
* FACEIT
*/
type FACEIT = (options: ProviderCommonOptions) => Provider<"faceit">
/**
* Foursquare
*/
type Foursquare = (options: ProviderCommonOptions) => Provider<"foursquare">
/**
* FusionAuth
*/
type FusionAuth = (options: ProviderFusionAuthOptions) => Provider<"fusionauth">
interface ProviderFusionAuthOptions extends ProviderCommonOptions {
tenantId?: string
domain?: string
}
/**
* Instagram
*/
type Instagram = (options: ProviderCommonOptions) => Provider<"instagram">
/**
* Kakao
*/
type Kakao = (options: ProviderCommonOptions) => Provider<"kakao">
/**
* LINE
*/
type LINE = (options: ProviderCommonOptions) => Provider<"line">
/**
* MailRu
*/
type MailRu = (options: ProviderCommonOptions) => Provider<"mailru">
/**
* Medium
*/
type Medium = (options: ProviderCommonOptions) => Provider<"medium">
/**
* Netlify
*/
type Netlify = (options: ProviderCommonOptions) => Provider<"netlify">
/**
* Osso
*/
type Osso = (options: ProviderCommonOptions) => Provider<"osso">
/**
* Salesforce
*/
type Salesforce = (options: ProviderCommonOptions) => Provider<"salesforce">
/**
* Strava
*/
type Strava = (options: ProviderCommonOptions) => Provider<"strava">
/**
* VK
*/
type VK = (options: ProviderCommonOptions) => Provider<"vk">
/**
* Zoho
*/
type Zoho = (options: ProviderCommonOptions) => Provider<"zoho">

View File

@@ -0,0 +1,26 @@
import Adapters, { TypeORMAdapter } from "next-auth/adapters"
// ExpectType TypeORMAdapter["Adapter"]
Adapters.Default({
type: "sqlite",
database: ":memory:",
synchronize: true,
})
// ExpectType TypeORMAdapter
Adapters.TypeORM.Adapter({
type: "sqlite",
database: ":memory:",
synchronize: true,
})
// ExpectType PrismaAdapter
Adapters.Prisma.Adapter({
prisma: {},
modelMapping: {
User: "foo",
Account: "bar",
Session: "session",
VerificationRequest: "foo",
},
})

View File

@@ -0,0 +1,83 @@
import * as client from "next-auth/client"
import { nextReq } from "./test-helpers"
const clientSession = {
user: {
name: "Bruce",
email: "bruce@lee.com",
image: "path/to/img",
},
accessToken: "123z",
expires: "1234",
}
// $ExpectType [Session | null | undefined, boolean]
client.useSession()
// $ExpectType Promise<Session | null>
client.getSession({ req: nextReq })
// $ExpectType Promise<Session | null>
client.session({ req: nextReq })
// $ExpectType Promise<Record<string, AppProvider> | null>
client.getProviders()
// $ExpectType Promise<Record<string, AppProvider> | null>
client.providers()
// $ExpectType Promise<string | null>
client.getCsrfToken({ req: nextReq })
// $ExpectType Promise<string | null>
client.csrfToken({ req: nextReq })
// $ExpectType Promise<void>
client.signin("github", { data: "foo", redirect: false }, { login: "username" })
// $ExpectType Promise<SignInResponse>
client.signin("credentials", { data: "foo", redirect: false })
// $ExpectType Promise<SignInResponse>
client.signin("email", { data: "foo", redirect: false })
// $ExpectType Promise<void>
client.signin("email", { data: "foo", redirect: true })
// $ExpectType Promise<void>
client.signout()
// $ExpectType Promise<void>
client.signout({ callbackUrl: "https://foo.com/callback", redirect: true })
// $ExpectType ReactElement<any, any> | null
client.Provider({
session: clientSession,
options: {
baseUrl: "https://foo.com",
basePath: "/",
clientMaxAge: 1234,
},
})
// $ExpectType ReactElement<any, any> | null
client.Provider({
session: clientSession,
})
// $ExpectType ReactElement<any, any> | null
client.Provider({
session: undefined,
options: {},
})
// $ExpectType ReactElement<any, any> | null
client.Provider({
session: null,
options: {
baseUrl: "https://foo.com",
basePath: "/",
clientMaxAge: 1234,
keepAlive: 4321,
},
})

26
types/tests/jwt.test.ts Normal file
View File

@@ -0,0 +1,26 @@
import * as JWTType from "next-auth/jwt"
import { nextReq } from "./test-helpers"
// $ExpectType Promise<string>
JWTType.encode({
token: { key: "value" },
secret: "secret",
})
// $ExpectType Promise<WithAdditionalParams<JWT>>
JWTType.decode({
token: "token",
secret: "secret",
})
// $ExpectType Promise<string>
JWTType.getToken({
req: nextReq,
raw: true,
})
// $ExpectType Promise<WithAdditionalParams<JWT>>
JWTType.getToken({
req: nextReq,
secret: "secret",
})

View File

@@ -0,0 +1,259 @@
import Providers from "next-auth/providers"
// $ExpectType NonNullParams<ProviderEmailOptions> & { id: "email"; type: "email"; }
Providers.Email({
server: "path/to/server",
from: "path/from",
})
// $ExpectType NonNullParams<ProviderEmailOptions> & { id: "email"; type: "email"; }
Providers.Email({
server: {
host: "host",
port: 123,
auth: {
user: "foo",
pass: "123",
},
},
from: "path/from",
})
// $ExpectType NonNullParams<ProviderCredentialsOptions> & { id: "credentials"; type: "credentials"; }
Providers.Credentials({
id: "login",
name: "account",
credentials: {
user: {
label: "Password",
type: "password",
},
password: {
label: "Password",
type: "password",
},
},
authorize: async (credentials) => {
const user = {
/* fetched user */
}
return user
},
})
// $ExpectType Provider<"apple", "oauth"> & { protection: "none"; }
Providers.Apple({
clientId: "foo123",
clientSecret: {
appleId: "foo@icloud.com",
teamId: "foo",
privateKey: "123xyz",
keyId: "1234",
},
})
// $ExpectType Provider<"twitter", "oauth">
Providers.Twitter({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"facebook", "oauth">
Providers.Facebook({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"github", "oauth">
Providers.GitHub({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"github", "oauth">
Providers.GitHub({
clientId: "foo123",
clientSecret: "bar123",
scope: "change:thing read:that",
})
// $ExpectType Provider<"gitlab", "oauth">
Providers.GitLab({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"slack", "oauth">
Providers.Slack({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"google", "oauth">
Providers.Google({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"google", "oauth">
Providers.Google({
clientId: "foo123",
clientSecret: "bar123",
authorizationUrl: "https://foo.google.com",
})
// $ExpectType Provider<"auth0", "oauth"> & { domain: string; }
Providers.Auth0({
clientId: "foo123",
clientSecret: "bar123",
domain: "https://foo.auth0.com",
})
// $ExpectType Provider<"auth0", "oauth"> & { domain: string; }
Providers.Auth0({
clientId: "foo123",
clientSecret: "bar123",
domain: "https://foo.auth0.com",
profile: () => ({
id: "foo123",
name: "foo",
email: "foo@bar.io",
image: "https://foo.auth0.com/image/1.png",
}),
})
// $ExpectType Provider<string, "oauth"> & { domain: string; }
Providers.IdentityServer4({
id: "identity-server4",
name: "IdentityServer4",
scope: "change:thing read:that",
domain: "https://foo.is4.com",
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"discord", "oauth">
Providers.Discord({
clientId: "foo123",
clientSecret: "bar123",
scope: "identify",
})
// $ExpectType Provider<"twitch", "oauth">
Providers.Twitch({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"okta", "oauth"> & { domain: string; }
Providers.Okta({
clientId: "foo123",
clientSecret: "bar123",
domain: "https://foo.auth0.com",
})
// $ExpectType Provider<"battlenet", "oauth"> & { region: string; }
Providers.BattleNet({
clientId: "foo123",
clientSecret: "bar123",
region: "europe",
})
// $ExpectType Provider<"box", "oauth">
Providers.Box({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"cognito", "oauth"> & { domain: string; }
Providers.Cognito({
clientId: "foo123",
clientSecret: "bar123",
domain: "https://foo.auth0.com",
})
// $ExpectType Provider<"yandex", "oauth">
Providers.Yandex({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"linkedin", "oauth">
Providers.LinkedIn({
clientId: "foo123",
clientSecret: "bar123",
scope: "r_emailaddress r_liteprofile",
})
// $ExpectType Provider<"spotify", "oauth">
Providers.Spotify({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"spotify", "oauth">
Providers.Spotify({
clientId: "foo123",
clientSecret: "bar123",
scope: "user-read-email",
})
// $ExpectType Provider<"basecamp", "oauth">
Providers.Basecamp({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"reddit", "oauth">
Providers.Reddit({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"azure-ad-b2c", "oauth">
Providers.AzureADB2C({
clientId: "foo123",
clientSecret: "bar123",
scope: "offline_access User.Read",
tenantId: "tenantId",
idToken: true,
})
// $ExpectType Provider<"fusionauth", "oauth">
Providers.FusionAuth({
name: "FusionAuth",
domain: "domain",
clientId: "clientId",
clientSecret: "clientSecret",
tenantId: "tenantId",
})
// $ExpectType Provider<"faceit", "oauth">
Providers.FACEIT({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"instagram", "oauth">
Providers.Instagram({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"kakao", "oauth">
Providers.Kakao({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"osso", "oauth">
Providers.Osso({
clientId: "foo123",
clientSecret: "bar123",
})
// $ExpectType Provider<"zoho", "oauth">
Providers.Zoho({
clientId: "foo123",
clientSecret: "bar123",
})

255
types/tests/server.test.ts Normal file
View File

@@ -0,0 +1,255 @@
import Providers, { AppProvider, Provider } from "next-auth/providers"
import Adapters, {
Adapter,
EmailAppProvider,
Profile,
Session,
VerificationRequest,
} from "next-auth/adapters"
import NextAuth, * as NextAuthTypes from "next-auth"
import { IncomingMessage, ServerResponse } from "http"
import * as JWTType from "next-auth/jwt"
import { Socket } from "net"
import { NextApiRequest, NextApiResponse } from "next"
const req: NextApiRequest = Object.assign(new IncomingMessage(new Socket()), {
query: {},
cookies: {},
body: {},
env: {},
})
const res: NextApiResponse = Object.assign(new ServerResponse(req), {
send: (body: string) => undefined,
json: (body: string) => undefined,
status: (code: number) => res,
redirect: (statusOrUrl: number | string, url?: string) => res as any,
setPreviewData: (data: object | string) => res,
clearPreviewData: () => res,
})
const pageOptions = {
signin: "path/to/signin",
signout: "path/to/signout",
error: "path/to/error",
verifyRequest: "path/to/verify",
newUsers: "path/to/signup",
}
const simpleConfig = {
site: "https://foo.com",
providers: [
Providers.GitHub({
clientId: "123",
clientSecret: "123",
scope:
"user public_repo repo repo_deployment repo:status read:repo_hook read:org read:public_key read:gpg_key",
}),
],
}
const exampleUser: NextAuthTypes.User = {
name: "",
image: "",
email: "",
}
const exampleSession: Session = {
userId: "",
accessToken: "",
sessionToken: "",
expires: new Date(),
}
const exampleVerificatoinRequest: VerificationRequest = {
identifier: "",
token: "",
expires: new Date(),
}
const adapter: Adapter<
NextAuthTypes.User,
Profile,
Session,
VerificationRequest
> = {
async getAdapter(appOptions: NextAuthTypes.AppOptions) {
return {
createUser: async (profile: Profile) => exampleUser,
getUser: async (id: string) => exampleUser,
getUserByEmail: async (email: string) => exampleUser,
getUserByProviderAccountId: async (
providerId: string,
providerAccountId: string
) => exampleUser,
updateUser: async (user: NextAuthTypes.User) => exampleUser,
linkAccount: async (
userId: string,
providerId: string,
providerType: string,
providerAccountId: string,
refreshToken: string,
accessToken: string,
accessTokenExpires: number
) => undefined,
createSession: async (user: NextAuthTypes.User) => exampleSession,
getSession: async (sessionToken: string) => exampleSession,
updateSession: async (session: Session, force?: boolean) =>
exampleSession,
deleteSession: async (sessionToken: string) => undefined,
createVerificationRequest: async (
email: string,
url: string,
token: string,
secret: string,
provider: EmailAppProvider,
options: NextAuthTypes.AppOptions
) => exampleVerificatoinRequest,
getVerificationRequest: async (
email: string,
verificationToken: string,
secret: string,
provider: AppProvider
) => exampleVerificatoinRequest,
deleteVerificationRequest: async (
email: string,
verificationToken: string,
secret: string,
provider: AppProvider
) => undefined,
}
},
}
const allConfig = {
providers: [
Providers.Twitter({
clientId: "123",
clientSecret: "123",
}),
],
database: "path/to/db",
debug: true,
secret: "my secret",
session: {
jwt: true,
maxAge: 365,
updateAge: 60,
},
jwt: {
secret: "secret-thing",
maxAge: 365,
encryption: true,
signingKey: "some-key",
encryptionKey: "some-key",
encode: async () => "foo",
decode: async () => ({}),
},
pages: pageOptions,
callbacks: {
async signIn(
user: NextAuthTypes.User,
account: Record<string, unknown>,
profile: Record<string, unknown>
) {
return true
},
async redirect(url: string, baseUrl: string) {
return "path/to/foo"
},
async session(
session: NextAuthTypes.Session,
userOrToken: NextAuthTypes.User
) {
return { ...session }
},
async jwt(
token: JWTType.JWT,
user?: NextAuthTypes.User,
account?: Record<string, unknown>,
profile?: Record<string, unknown>,
isNewUser?: boolean
) {
return token
},
},
events: {
async signIn(message: string) {
return undefined
},
async signOut(message: string) {
return undefined
},
async createUser(message: string) {
return undefined
},
async linkAccount(message: string) {
return undefined
},
async session(message: string) {
return undefined
},
async error(message: string) {
return undefined
},
},
adapter,
useSecureCookies: true,
cookies: {
sessionToken: {
name: "__Secure-next-auth.session-token",
options: {
httpOnly: true,
sameSite: true as true,
path: "/",
secure: true,
domain: "foo.com",
},
},
},
}
const customProvider: Provider<"google"> = {
id: "google",
name: "Google",
type: "oauth",
version: "2.0",
scope:
"https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
params: { grant_type: "authorization_code" },
accessTokenUrl: "https://accounts.google.com/o/oauth2/token",
requestTokenUrl: "https://accounts.google.com/o/oauth2/auth",
authorizationUrl:
"https://accounts.google.com/o/oauth2/auth?response_type=code",
profileUrl: "https://www.googleapis.com/oauth2/v1/userinfo?alt=json",
async profile(profile, tokens) {
return {
id: profile.id,
name: profile.name,
email: profile.email,
image: profile.picture,
}
},
clientId: "",
clientSecret: "",
}
const customProviderConfig = {
site: "https://foo.com",
providers: [customProvider],
}
// $ExpectType void | Promise<void>
NextAuth(simpleConfig)
// $ExpectType void | Promise<void>
NextAuth(allConfig)
// $ExpectType void | Promise<void>
NextAuth(customProviderConfig)
// $ExpectType void | Promise<void>
NextAuth(req, res, simpleConfig)
// $ExpectType void | Promise<void>
NextAuth(req, res, allConfig)

View File

@@ -0,0 +1,13 @@
import { IncomingMessage, ServerResponse } from "http"
import { Socket } from "net"
import { NextApiRequest } from "next"
export const nextReq: NextApiRequest = Object.assign(
new IncomingMessage(new Socket()),
{
query: {},
cookies: {},
body: {},
env: {},
}
)

23
types/tsconfig.json Normal file
View File

@@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": ["es6", "dom"],
"jsx": "react",
"noImplicitAny": true,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"esModuleInterop": true,
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"next-auth": ["."],
"next-auth/providers": ["./providers"],
"next-auth/adapters": ["./adapters"],
"next-auth/client": ["./client"],
"next-auth/jwt": ["./jwt"],
"next": ["./_next"]
}
}
}

6
types/tslint.json Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": "dtslint/dtslint.json",
"rules": {
"semicolon": false
}
}

View File

@@ -1,22 +1,77 @@
---
id: typescript
title: TypeScript Support
title: TypeScript
---
Currently, NextAuth.js relies on the community to provide TypeScript types. You can download it from [DefinitelyTyped](https://www.npmjs.com/package/@types/next-auth).
NextAuth.js comes with its own types, so you can safely use it in your TypeScript projects. Even if you don't use TypeScript, IDEs like VSCode will pick this up, to provide you with a better developer experience. While you are typing, you will get suggestions of what certain objects are, and sometimes also links to documentation, and examples.
Add it to your project with:
:::note
The types at [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) under the name of `@types/next-auth` are now deprecated, and not maintained anymore.
:::
```sh
npm i -D @types/next-auth
***
## Module Augmentaion
`next-auth` comes with certain types/interfaces, that are shared across submodules. Good examples are `Session` and `JWT`. Ideally, you should only need to create these types at a single place, and TS should pick them up in every location where they are referenced. Luckily, this is exactly what Module Agumentation can do for us. Define your shared interfaces in a single location, and get type-safety across your application, when you use `next-auth` (or one of its submodules).
1. Let's look at `Session`:
```ts title="pages/api/[...nextauth].ts"
import NextAuth from "next-auth"
export default NextAuth({
callbacks: {
session(session, token) {
return session // The type here should match the one returned in `useSession()`
}
}
})
```
or
```ts title="pages/index.ts"
import { useSession } from "next-auth/client"
```sh
yarn add -D @types/next-auth
export default function IndexPage() {
// `session` should match `callbacks.session()` in `NextAuth()`
const [session] = useSession()
return (
// Your component
)
}
```
You can find an initial Pull Request at [next-auth#516](https://github.com/nextauthjs/next-auth/pull/516) adding TypeScript. At the time of this writing, it looks like we would like to go from a complete migration to a more relaxed, incremental rewrite.
To extend/augment this type, create a `types/next-auth.d.ts` file in your project:
Feel free to open a Pull Request, if you would like to contribute!
```ts title="types/next-auth.d.ts"
import NextAuth from "next-auth"
declare module "next-auth" {
interface Session {
user: {
/** The user's postal address. */
address: string
}
}
}
```
Make sure that the `types` folder is added to [`typeRoots`](https://www.typescriptlang.org/tsconfig/#typeRoots) in your project's `tsconfig.json` file.
2. Check out `JWT` also:
```ts title="types/next-auth.d.ts"
declare module "next-auth/jwt" {
interface JWT {
/** OpenID ID Token */
idToken?: string
}
}
```
Note that this time we declared `JWT` inside `next-auth/jwt`, as this is its default location.
## Contributing
Contributions of any kind are always welcome, especially for TypeScript. Please keep in mind that we are a small team working on this project in our free time. We will try our best to give support, but if you think you have a solution for a problem, please open a PR!