Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple fixes/improvements to auth-nextjs #785

Merged
merged 9 commits into from
Nov 22, 2023
5 changes: 3 additions & 2 deletions packages/auth-core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

static async create(client: edgedb.Client) {
const connectConfig: ResolvedConnectConfig = (
await (client as any).pool._getNormalizedConnectConfig()

Check warning on line 31 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 31 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 31 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Unexpected any. Specify a different type

Check warning on line 31 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Unexpected any. Specify a different type

Check warning on line 31 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Unexpected any. Specify a different type

Check warning on line 31 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Unexpected any. Specify a different type
).connectionParams;

const [host, port] = connectConfig.address;
Expand All @@ -40,7 +40,7 @@
}

/** @internal */
public async _get<T extends any = unknown>(path: string): Promise<T> {

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Unexpected any. Specify a different type

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Unexpected any. Specify a different type

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Unexpected any. Specify a different type

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 43 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Unexpected any. Specify a different type
const res = await fetch(new URL(path, this.baseUrl), {
method: "get",
});
Expand All @@ -50,13 +50,13 @@
if (res.headers.get("content-type")?.startsWith("application/json")) {
return res.json();
}
return null as any;

Check warning on line 53 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 53 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 53 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Unexpected any. Specify a different type

Check warning on line 53 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Unexpected any. Specify a different type

Check warning on line 53 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Unexpected any. Specify a different type

Check warning on line 53 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Unexpected any. Specify a different type
}

/** @internal */
public async _post<T extends any = unknown>(

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Unexpected any. Specify a different type

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Unexpected any. Specify a different type

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Unexpected any. Specify a different type

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Constraining the generic type `T` to `any` does nothing and is unnecessary

Check warning on line 57 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Unexpected any. Specify a different type
path: string,
body?: any

Check warning on line 59 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 59 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 59 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Unexpected any. Specify a different type

Check warning on line 59 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Unexpected any. Specify a different type

Check warning on line 59 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Unexpected any. Specify a different type

Check warning on line 59 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Unexpected any. Specify a different type
): Promise<T> {
const res = await fetch(new URL(path, this.baseUrl), {
method: "post",
Expand All @@ -73,7 +73,7 @@
if (res.headers.get("content-type")?.startsWith("application/json")) {
return res.json();
}
return null as any;

Check warning on line 76 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 76 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Unexpected any. Specify a different type

Check warning on line 76 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Unexpected any. Specify a different type

Check warning on line 76 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Unexpected any. Specify a different type

Check warning on line 76 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Unexpected any. Specify a different type

Check warning on line 76 in packages/auth-core/src/core.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Unexpected any. Specify a different type
}

async createPKCESession() {
Expand Down Expand Up @@ -106,7 +106,7 @@
password: string,
verifyUrl: string
): Promise<
| { status: "complete"; tokenData: TokenData }
| { status: "complete"; verifier: string; tokenData: TokenData }
| { status: "verificationRequired"; verifier: string }
> {
const { challenge, verifier } = await pkce.createVerifierChallengePair();
Expand All @@ -122,6 +122,7 @@
if ("code" in result) {
return {
status: "complete",
verifier,
tokenData: await this.getToken(result.code, verifier),
};
} else {
Expand All @@ -145,7 +146,7 @@
}

async sendPasswordResetEmail(email: string, resetUrl: string) {
const { challenge, verifier } = pkce.createVerifierChallengePair();
const { challenge, verifier } = await pkce.createVerifierChallengePair();
return {
verifier,
...(await this._post<{ email_sent: string }>("send-reset-email", {
Expand Down
118 changes: 87 additions & 31 deletions packages/auth-nextjs/src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,53 @@
import { redirect } from "next/navigation";
import type { NextRequest } from "next/server";

import { NextAuth, NextAuthSession, type NextAuthOptions } from "../shared";
import {

Check failure on line 12 in packages/auth-nextjs/src/app/index.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Import "BuiltinProviderNames" is only used as types

Check failure on line 12 in packages/auth-nextjs/src/app/index.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Import "BuiltinProviderNames" is only used as types

Check failure on line 12 in packages/auth-nextjs/src/app/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Import "BuiltinProviderNames" is only used as types

Check failure on line 12 in packages/auth-nextjs/src/app/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Import "BuiltinProviderNames" is only used as types

Check failure on line 12 in packages/auth-nextjs/src/app/index.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Import "BuiltinProviderNames" is only used as types

Check failure on line 12 in packages/auth-nextjs/src/app/index.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Import "BuiltinProviderNames" is only used as types
NextAuth,
NextAuthSession,
type NextAuthOptions,
BuiltinProviderNames,
} from "../shared";

export { NextAuthSession, type NextAuthOptions };
export {
NextAuthSession,
type NextAuthOptions,
type BuiltinProviderNames,
type TokenData,
};

type ParamsOrError<Result extends object> =
| ({ error: null } & Result)
| ({ error: Error } & { [Key in keyof Result]?: undefined });
type ParamsOrError<Result extends object, ErrorDetails extends object = {}> =
| ({ error: null } & { [Key in keyof ErrorDetails]?: undefined } & Result)
| ({ error: Error } & ErrorDetails & { [Key in keyof Result]?: undefined });
jaclarke marked this conversation as resolved.
Show resolved Hide resolved

export interface CreateAuthRouteHandlers {
onOAuthCallback(
params: ParamsOrError<{ tokenData: TokenData; isSignUp: boolean }>
params: ParamsOrError<{
tokenData: TokenData;
provider: BuiltinOAuthProviderNames;
isSignUp: boolean;
}>
): void;
onEmailPasswordSignIn(params: ParamsOrError<{ tokenData: TokenData }>): void;
onEmailPasswordSignUp(
params: ParamsOrError<{ tokenData: TokenData | null }>
): void;
onEmailPasswordReset(params: ParamsOrError<{ tokenData: TokenData }>): void;
onEmailVerify(params: ParamsOrError<{ tokenData: TokenData }>): void;
onEmailVerify(
params: ParamsOrError<
{ tokenData: TokenData },
{ verificationToken?: string }
>
): void;
onBuiltinUICallback(
params: ParamsOrError<{ tokenData: TokenData | null; isSignUp: boolean }>
params: ParamsOrError<
(
| {
tokenData: TokenData;
provider: BuiltinProviderNames;
}
| { tokenData: null; provider: null }
) & { isSignUp: boolean }
>
): void;
onSignout(): void;
}
Expand All @@ -48,6 +75,10 @@
);
}

async getProvidersInfo() {
return (await this.core).getProvidersInfo();
}

createAuthRouteHandlers({
onOAuthCallback,
onEmailPasswordSignIn,
Expand Down Expand Up @@ -137,7 +168,14 @@
});
cookies().delete(this.options.pkceVerifierCookieName);

return onOAuthCallback({ error: null, tokenData, isSignUp });
return onOAuthCallback({
error: null,
tokenData,
provider: req.nextUrl.searchParams.get(
"provider"
) as BuiltinOAuthProviderNames,
isSignUp,
});
}
case "emailpassword/verify": {
if (!onEmailVerify) {
Expand All @@ -158,6 +196,7 @@
if (!verifier) {
return onEmailVerify({
error: new Error("no pkce verifier cookie found"),
verificationToken,
});
}
let tokenData: TokenData;
Expand All @@ -168,6 +207,7 @@
} catch (err) {
return onEmailVerify({
error: err instanceof Error ? err : new Error(String(err)),
verificationToken,
});
}
cookies().set({
Expand Down Expand Up @@ -203,6 +243,7 @@
return onBuiltinUICallback({
error: null,
tokenData: null,
provider: null,
isSignUp: true,
});
}
Expand Down Expand Up @@ -236,7 +277,14 @@
});
cookies().delete(this.options.pkceVerifierCookieName);

return onBuiltinUICallback({ error: null, tokenData, isSignUp });
return onBuiltinUICallback({
error: null,
tokenData,
provider: req.nextUrl.searchParams.get(
"provider"
) as BuiltinProviderNames,
isSignUp,
});
}
case "builtin/signin":
case "builtin/signup": {
Expand Down Expand Up @@ -328,6 +376,12 @@
error: err instanceof Error ? err : new Error(String(err)),
});
}
cookies().set({
name: this.options.pkceVerifierCookieName,
value: result.verifier,
httpOnly: true,
sameSite: "strict",
});
if (result.status === "complete") {
cookies().set({
name: this.options.authCookieName,
Expand All @@ -340,18 +394,12 @@
tokenData: result.tokenData,
});
} else {
cookies().set({
name: this.options.pkceVerifierCookieName,
value: result.verifier,
httpOnly: true,
sameSite: "strict",
});
return onEmailPasswordSignUp({ error: null, tokenData: null });
}
}
case "emailpassword/send-reset-email": {
if (!this.options.passwordResetUrl) {
throw new Error(`'passwordResetUrl' option not configured`);
if (!this.options.passwordResetPath) {
throw new Error(`'passwordResetPath' option not configured`);
}
const [email] = _extractParams(
await _getReqBody(req),
Expand All @@ -360,7 +408,13 @@
);
const { verifier } = await (
await this.core
).sendPasswordResetEmail(email, this.options.passwordResetUrl);
).sendPasswordResetEmail(
email,
new URL(
this.options.passwordResetPath,
this.options.baseUrl
).toString()
);
cookies().set({
name: this.options.pkceVerifierCookieName,
value: verifier,
Expand Down Expand Up @@ -465,6 +519,12 @@
password,
`${this._authRoute}/emailpassword/verify`
);
cookies().set({
name: this.options.pkceVerifierCookieName,
value: result.verifier,
httpOnly: true,
sameSite: "strict",
});
if (result.status === "complete") {
cookies().set({
name: this.options.authCookieName,
Expand All @@ -473,28 +533,24 @@
sameSite: "strict",
});
return result.tokenData;
} else {
cookies().set({
name: this.options.pkceVerifierCookieName,
value: result.verifier,
httpOnly: true,
sameSite: "strict",
});
return null;
}
return null;
},
emailPasswordSendPasswordResetEmail: async (
data: FormData | { email: string }
) => {
if (!this.options.passwordResetUrl) {
throw new Error(`'passwordResetUrl' option not configured`);
if (!this.options.passwordResetPath) {
throw new Error(`'passwordResetPath' option not configured`);
}
const [email] = _extractParams(data, ["email"], "email missing");
const { verifier } = await (
await this.core
).sendPasswordResetEmail(
email,
`${this.options.baseUrl}/${this.options.passwordResetUrl}`
new URL(
this.options.passwordResetPath,
this.options.baseUrl
).toString()
);
cookies().set({
name: this.options.pkceVerifierCookieName,
Expand All @@ -504,7 +560,7 @@
});
},
emailPasswordResetPassword: async (
data: FormData | { resetToken: string; password: string }
data: FormData | { reset_token: string; password: string }
) => {
const verifier = cookies().get(
this.options.pkceVerifierCookieName
Expand Down
26 changes: 19 additions & 7 deletions packages/auth-nextjs/src/shared.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { Client } from "edgedb";

Check failure on line 1 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 1 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 1 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 1 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 1 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

All imports in the declaration are only used as types. Use `import type`

Check failure on line 1 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

All imports in the declaration are only used as types. Use `import type`
import { Auth, BuiltinOAuthProviderNames } from "@edgedb/auth-core";
import {

Check failure on line 2 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest, stable)

Imports "BuiltinOAuthProviderNames" and "emailPasswordProviderName" are only used as types

Check failure on line 2 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest, stable)

Imports "BuiltinOAuthProviderNames" and "emailPasswordProviderName" are only used as types

Check failure on line 2 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, nightly)

Imports "BuiltinOAuthProviderNames" and "emailPasswordProviderName" are only used as types

Check failure on line 2 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 3)

Imports "BuiltinOAuthProviderNames" and "emailPasswordProviderName" are only used as types

Check failure on line 2 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 18, 2)

Imports "BuiltinOAuthProviderNames" and "emailPasswordProviderName" are only used as types

Check failure on line 2 in packages/auth-nextjs/src/shared.ts

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18, stable)

Imports "BuiltinOAuthProviderNames" and "emailPasswordProviderName" are only used as types
Auth,
BuiltinOAuthProviderNames,
emailPasswordProviderName,
} from "@edgedb/auth-core";

export type BuiltinProviderNames =
| BuiltinOAuthProviderNames
| typeof emailPasswordProviderName;

export interface NextAuthOptions {
baseUrl: string;
authRoutesPath?: string;
authCookieName?: string;
pkceVerifierCookieName?: string;
passwordResetUrl?: string;
passwordResetPath?: string;
}

type OptionalOptions = "passwordResetUrl";
type OptionalOptions = "passwordResetPath";

export abstract class NextAuth {
/** @internal */
Expand All @@ -24,7 +32,7 @@
authCookieName: options.authCookieName ?? "edgedb-session",
pkceVerifierCookieName:
options.pkceVerifierCookieName ?? "edgedb-pkce-verifier",
passwordResetUrl: options.passwordResetUrl,
passwordResetPath: options.passwordResetPath,
};
}

Expand Down Expand Up @@ -66,8 +74,12 @@

async isLoggedIn() {
if (!this.authToken) return false;
return (await this.client.querySingle(
`select exists global ext::auth::ClientTokenIdentity`
)) as boolean;
try {
return await this.client.querySingle<boolean>(
`select exists global ext::auth::ClientTokenIdentity`
);
} catch {
return false;
}
}
}
Loading