diff --git a/types/next-auth/_utils.d.ts b/types/next-auth/_utils.d.ts new file mode 100644 index 0000000000..b008cf50b1 --- /dev/null +++ b/types/next-auth/_utils.d.ts @@ -0,0 +1,25 @@ +import { IncomingMessage, ServerResponse } from 'http'; + +interface GenericObject { + [key: string]: any; +} + +interface NextApiRequest extends IncomingMessage, GenericObject { + query: { + [key: string]: string | string[]; + }; + cookies: { + [key: string]: string; + }; + body: any; +} + +interface NextApiResponse extends ServerResponse, GenericObject { + send: Send; + json: Send; + status: (statusCode: number) => NextApiResponse; +} + +type Send = (body: T) => void; + +export { GenericObject, NextApiRequest, NextApiResponse }; diff --git a/types/next-auth/adapters.d.ts b/types/next-auth/adapters.d.ts index 2cfda0805a..b73f5d12dc 100644 --- a/types/next-auth/adapters.d.ts +++ b/types/next-auth/adapters.d.ts @@ -1,24 +1,183 @@ -import { ConnectionOptions } from 'typeorm'; +import { ConnectionOptions, EntitySchema } from 'typeorm'; +import { AppOptions } from '.'; +import { SessionProvider } from './client'; -/** - * TODO: type adapters correctly - * @see https://next-auth.js.org/schemas/adapters - */ -interface GenericObject { - [key: string]: any; +// NOTE: There are a lot of `any`. That's because there could be any schema for each entity +interface Adapter { + getAdapter( + appOptions: AppOptions, + ): Promise<{ + createUser(profile: any): Promise; + getUser(id: string): Promise; + getUserByEmail(email: string): Promise; + getUserByProviderAccountId(providerId: string, providerAccountId: string): Promise; + updateUser(profile: any): Promise; + linkAccount( + userId: string, + providerId: string, + providerType: string, + providerAccountId: string, + refreshToken: string, + accessToken: string, + accessTokenExpires: number, + ): Promise; + createSession(user: any): Promise; + getSession(sessionToken: string): Promise; + updateSession(session: any): Promise; + deleteSession(sessionToken: string): Promise; + createVerificationRequest?( + email: string, + url: string, + token: string, + secret: string, + provider: SessionProvider, + options: AppOptions, + ): Promise; + getVerificationRequest?( + email: string, + verificationToken: string, + secret: string, + provider: SessionProvider, + ): Promise; + deleteVerificationRequest?( + email: string, + verificationToken: string, + secret: string, + provider: SessionProvider, + ): Promise; + }>; } -type Adapter = (config: ConnectionOptions) => any; +type Schema = EntitySchema['options']; interface Adapters { - Default: Adapter; - TypeORM: { - Adapter: Adapter; - Models: GenericObject; + 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; + }; + User?: { + model: U; + schema: Schema; + }; + Session?: { + model: S; + schema: Schema; + }; + VerificationRequest?: { + model: VR; + schema: Schema; + }; + }; + }, + ): Adapter; + Models: { + Account: { + model: TypeORMAccountModel; + schema: Schema; + }; + User: { + model: TypeORMUserModel; + schema: Schema; + }; + Session: { + model: TypeORMSessionModel; + schema: Schema; + }; + VerificationRequest: { + model: TypeORMVerificationRequestModel; + schema: Schema; + }; }; } +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 { + name?: string; + email?: string; + image?: string; + emailVerified?: Date; + + constructor(name?: string, email?: string, image?: string, emailVerified?: Date); +} + +declare class TypeORMSessionModel { + userId: number; + expires: Date; + sessionToken: string; + accessToken: string; + + constructor(userId: number, expires: Date, sessionToken?: string, accessToken?: string); +} + +declare class TypeORMVerificationRequestModel { + identifier?: string; + token?: string; + expires?: Date; + + constructor(identifier?: string, token?: string, expires?: Date); +} + export default Adapters; -export { Adapter }; +export { + Adapter, + Adapters, + TypeORMAdapter, + TypeORMAccountModel, + TypeORMUserModel, + TypeORMSessionModel, + TypeORMVerificationRequestModel, + PrismaAdapter, +}; diff --git a/types/next-auth/client.d.ts b/types/next-auth/client.d.ts index a02b16f179..2d71939b5b 100644 --- a/types/next-auth/client.d.ts +++ b/types/next-auth/client.d.ts @@ -1,6 +1,8 @@ import { FC } from 'react'; +import { IncomingMessage } from 'http'; +import { GenericObject } from './_utils'; -export interface Session { +interface Session { user: { name: string; email: string; @@ -14,7 +16,7 @@ interface GetProvidersResponse { [provider: string]: SessionProvider; } -interface SessionProvider { +interface SessionProvider extends GenericObject { id: string; name: string; type: string; @@ -22,37 +24,35 @@ interface SessionProvider { callbackUrl: string; } -interface GenericObject { - [key: string]: any; -} - interface ContextProviderProps { session: Session; - options?: ContextProviderOptions; -} - -interface ContextProviderOptions { - site?: string; - basePath?: string; - clientMaxAge?: number; - keepAlive?: number; + options?: SetOptionsParams; } interface SetOptionsParams { baseUrl?: string; basePath?: string; - clientMaxAge?: string; - keepAlive?: boolean; + clientMaxAge?: number; + keepAlive?: number; } type ContextProvider = FC; +interface NextContext { + req?: IncomingMessage; + ctx?: { req: IncomingMessage }; +} + declare function useSession(): [Session, boolean]; -declare function providers(context?: NextPageContext): Promise; +declare function providers(): Promise; declare const getProviders: typeof providers; -declare function session(context?: NextPageContext): Promise; +declare function session( + context?: NextContext & { + triggerEvent?: boolean; + }, +): Promise; declare const getSession: typeof session; -declare function csrfToken(context?: NextPageContext): Promise; +declare function csrfToken(context?: NextContext): Promise; declare const getCsrfToken: typeof csrfToken; declare function signin( provider?: string, @@ -82,30 +82,6 @@ export { options, setOptions, Provider, + Session, + SessionProvider, }; - -/** - * TODO: `dtslint` throws when parsing Next types... the following types are copied directly from `next/types` ... - * @see https://github.com/microsoft/dtslint/issues/297 - */ - -interface NextApiRequest { - query: { - [key: string]: string | string[]; - }; - cookies: { - [key: string]: string; - }; - body: any; - env: Env; -} - -interface NextPageContext { - req?: NextApiRequest; - ctx?: NextPageContext; - triggerEvent?: boolean; -} - -interface Env { - [key: string]: string; -} diff --git a/types/next-auth/index.d.ts b/types/next-auth/index.d.ts index 005babe8be..a162d83d1f 100644 --- a/types/next-auth/index.d.ts +++ b/types/next-auth/index.d.ts @@ -2,6 +2,7 @@ // Project: https://github.com/iaincollins/next-auth#readme // Definitions by: Lluis // Iain +// Juan // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 3.1 @@ -10,10 +11,13 @@ import { ConnectionOptions } from 'typeorm'; import { PossibleProviders } from './providers'; import { Adapter } from './adapters'; +import { GenericObject, NextApiRequest, NextApiResponse } from './_utils'; +import { SessionProvider } from './client'; +import { JWTEncodeParams, JWTDecodeParams } from './jwt'; -export interface InitOptions { +interface InitOptions { providers: Array>; - database?: ConnectionOptions | string; + database?: string | ConnectionOptions; secret?: string; session?: Session; jwt?: JWTOptions; @@ -26,7 +30,29 @@ export interface InitOptions { cookies?: Cookies; } -export interface PageOptions { +interface AppOptions { + debug: boolean; + pages: PageOptions; + adapter: Adapter; + baseUrl: string; + basePath: string; + action: 'providers' | 'session' | 'csrf' | 'signin' | 'signout' | 'callback' | 'verify-request' | 'error'; + provider?: string; + cookies: Cookies; + secret: string; + csrfToken: string; + providers: { + [provider: string]: SessionProvider; + }; + session: Session; + jwt: JWTOptions; + events: Events; + callbacks: Callbacks; + callbackUrl: string; + // redirect?(redirectUrl: string): any; +} + +interface PageOptions { signIn?: string; signOut?: string; error?: string; @@ -43,22 +69,22 @@ interface Cookie { options: CookieOptions; } -export interface CookieOptions { +interface CookieOptions { httpOnly?: boolean; - // TODO: type available `sameSite` identifiers - sameSite: string; - path: string; - secure: boolean; + sameSite?: true | 'strict' | 'lax' | 'none'; + path?: string; + secure?: boolean; + maxAge?: number; } interface Events { - signIn?(message: string): Promise; - signOut?(message: string): Promise; - createUser?(message: string): Promise; - updateUser?(message: string): Promise; - linkAccount?(message: string): Promise; - session?(message: string): Promise; - error?(message: string): Promise; + signIn?(message: any): Promise; + signOut?(message: any): Promise; + createUser?(message: any): Promise; + updateUser?(message: any): Promise; + linkAccount?(message: any): Promise; + session?(message: any): Promise; + error?(message: any): Promise; } interface Session { @@ -68,54 +94,14 @@ interface Session { } interface JWTOptions { - secret: string; + secret?: string; maxAge?: number; encode?(options: JWTEncodeParams): Promise; decode?(options: JWTDecodeParams): Promise; } -interface JWTSignInOptions { - expiresIn: string; -} - -interface JWTVerificationOptions { - maxTokenAge: string; - algorithms: string[]; -} - -interface JWTDecryptionOptions { - algorithms: string[]; -} - -interface JWTDecodeParams { - secret: string; - token: GenericObject; - maxAge?: number; - signingKey?: string; - verificationKey?: string; - verificationOptions?: JWTVerificationOptions; - decryptionKey?: string; - decryptionOptions?: JWTDecryptionOptions; - encryption?: boolean; -} - -interface JWTEncodeParams { - secret: string; - token: GenericObject; - signingKey?: string; - encryptionKey?: string; - signingOptions?: JWTSignInOptions; - encryptionOptions?: GenericObject; - encryption?: boolean; - maxAge?: number; -} - -interface GenericObject { - [key: string]: any; -} - // TODO: Improve callback typings -export interface Callbacks { +interface Callbacks { signIn?(user: GenericObject, account: GenericObject, profile: GenericObject): Promise; redirect?(url: string, baseUrl: string): Promise; session?(session: Session, user: GenericObject): Promise; @@ -130,38 +116,4 @@ export interface Callbacks { declare function NextAuth(req: NextApiRequest, res: NextApiResponse, options?: InitOptions): Promise; export default NextAuth; - -/** - * TODO: `dtslint` throws when parsing Next types... the following types are copied directly from `next/types` ... - * @see https://github.com/microsoft/dtslint/issues/297 - */ - -interface NextApiRequest { - query: { - [key: string]: string | string[]; - }; - cookies: { - [key: string]: string; - }; - body: any; - env: Env; -} - -interface NextApiResponse { - send: Send; - json: Send; - status: (statusCode: number) => NextApiResponse; - setPreviewData: ( - data: object | string, - options?: { - maxAge?: number; - }, - ) => NextApiResponse; - clearPreviewData: () => NextApiResponse; -} - -interface Env { - [key: string]: string; -} - -type Send = (body: T) => void; +export { InitOptions, AppOptions, PageOptions, Cookies, Events, Session, JWTOptions, Callbacks }; diff --git a/types/next-auth/jwt.d.ts b/types/next-auth/jwt.d.ts index 17b7a17efc..9c205adb40 100644 --- a/types/next-auth/jwt.d.ts +++ b/types/next-auth/jwt.d.ts @@ -1,24 +1,18 @@ import jose from 'jose'; +import { NextApiRequest } from './_utils'; -/** - * TODO: `dtslint` throws when parsing Next types... the following types are copied directly from `next/types` ... - * @see https://github.com/microsoft/dtslint/issues/297 - */ - -interface NextApiRequest { - query: { - [key: string]: string | string[]; - }; - cookies: { - [key: string]: string; - }; - body: any; - env: { - [key: string]: string; - }; +export interface JWTEncodeParams { + token?: object; + maxAge?: number; + secret: string | Buffer; + signingKey?: string; + signingOptions?: jose.JWT.SignOptions; + encryptionKey?: string; + encryptionOptions?: object; + encryption?: boolean; } -interface DecodeArgs { +export interface JWTDecodeParams { maxAge?: number; secret: string | Buffer; signingKey?: string; @@ -30,21 +24,8 @@ interface DecodeArgs { encryption?: boolean; } -declare function encode(args?: { - token?: object; - maxAge?: number; - secret: string | Buffer; - signingKey?: string; - signingOptions?: jose.JWT.SignOptions; - encryptionKey?: string; - encryptionOptions?: object; - encryption?: boolean; -}): Promise; -declare function decode( - args?: DecodeArgs & { - token: string; - }, -): Promise; +declare function encode(args?: JWTEncodeParams): Promise; +declare function decode(args?: JWTDecodeParams & { token: string }): Promise; declare function getToken( args?: { @@ -52,7 +33,7 @@ declare function getToken( secureCookie?: boolean; cookieName?: string; raw?: string; - } & DecodeArgs, + } & JWTDecodeParams, ): Promise; declare function getToken(args?: { req: NextApiRequest; diff --git a/types/next-auth/next-auth-tests.ts b/types/next-auth/next-auth-tests.ts index e941d80d24..0e99965661 100644 --- a/types/next-auth/next-auth-tests.ts +++ b/types/next-auth/next-auth-tests.ts @@ -5,48 +5,30 @@ * in the sense of typing and call signature consistency. They * are not intended as functional tests. */ -import NextAuth from 'next-auth'; import Providers from 'next-auth/providers'; import Adapters from 'next-auth/adapters'; import * as client from 'next-auth/client'; import * as JWT from 'next-auth/jwt'; +import NextAuth, * as NextAuthTypes from 'next-auth'; +import { GenericObject, NextApiRequest, NextApiResponse } from 'next-auth/_utils'; +import { IncomingMessage, ServerResponse } from 'http'; +import { Socket } from 'net'; // -------------------------------------------------------------------------- // Server // -------------------------------------------------------------------------- -interface GenericObject { - [key: string]: any; -} +const req: NextApiRequest = Object.assign(new IncomingMessage(new Socket()), { + query: {}, + cookies: {}, + body: {}, +}); -interface Session { - jwt?: boolean; - maxAge?: number; - updateAge?: number; -} - -const req = { - query: { - foo: 'bar', - }, - cookies: { - bar: 'baz', - }, - body: { - bam: 'bom', - }, - env: { - SOMETHING: 'SOMETHING', - }, -}; - -const res = { +const res: NextApiResponse = Object.assign(new ServerResponse(req), { send: () => undefined, json: () => undefined, status: (code: number) => res, - setPreviewData: (data: object | string) => res, - clearPreviewData: () => res, -}; +}); const pageOptions = { signin: 'path/to/signin', @@ -94,7 +76,7 @@ const allConfig = { callbacks: { signIgn: (user: GenericObject, account: GenericObject, profile: GenericObject) => Promise.resolve(true), redirect: (url: string, baseUrl: string) => Promise.resolve('path/to/foo'), - session: (session: Session, user: GenericObject) => Promise.resolve(user), + session: (session: NextAuthTypes.Session, user: GenericObject) => Promise.resolve(user), jwt: ( token: GenericObject, user: GenericObject, @@ -123,17 +105,29 @@ const allConfig = { return undefined; }, }, - adapter: () => { - async function getAdapter() { + adapter: { + getAdapter: (appOptions: NextAuthTypes.AppOptions) => { return Promise.resolve({ - getSession: async function getSession() { - return null; - }, + createUser: (profile: any) => Promise.resolve({}), + getUser: (id: string) => Promise.resolve({}), + getUserByEmail: (email: string) => Promise.resolve({}), + getUserByProviderAccountId: (providerId: string, providerAccountId: string) => Promise.resolve({}), + updateUser: (profile: any) => Promise.resolve({}), + linkAccount: ( + userId: string, + providerId: string, + providerType: string, + providerAccountId: string, + refreshToken: string, + accessToken: string, + accessTokenExpires: number, + ) => Promise.resolve(), + createSession: (user: any) => Promise.resolve({}), + getSession: (sessionToken: string) => Promise.resolve({}), + updateSession: (session: any) => Promise.resolve(), + deleteSession: (sessionToken: string) => Promise.resolve(), }); - } - return { - getAdapter, - }; + }, }, useSecureCookies: true, cookies: { @@ -141,7 +135,7 @@ const allConfig = { name: `__Secure-next-auth.session-token`, options: { httpOnly: true, - sameSite: 'foo', + sameSite: true as true, path: '/', secure: true, }, @@ -177,11 +171,6 @@ const baseContext = { triggerEvent: false, }; -const pageContext = { - ...baseContext, - ctx: { ...baseContext }, -}; - const githubProvider = { id: '123', name: 'github', @@ -204,22 +193,22 @@ const session = { client.useSession(); // $ExpectType Promise -client.getSession(pageContext); +client.getSession({ req }); // $ExpectType Promise -client.session(pageContext); +client.session({ req }); // $ExpectType Promise -client.getProviders(pageContext); +client.getProviders(); // $ExpectType Promise -client.providers(pageContext); +client.providers(); // $ExpectType Promise -client.getCsrfToken(pageContext); +client.getCsrfToken({ req }); // $ExpectType Promise -client.csrfToken(pageContext); +client.csrfToken({ req }); // $ExpectType Promise client.signin('github', { data: 'foo' }); @@ -234,7 +223,7 @@ client.signout({ callbackUrl: 'https://foo.com/callback' }); client.Provider({ session, options: { - site: 'https://foo.com', + baseUrl: 'https://foo.com', basePath: '/', clientMaxAge: 1234, },