nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
25.11k stars 3.55k forks source link

signIn response object always showing values of status = 200 and ok = true even when client submits invalid credentials #10931

Open stephendewyer opened 6 months ago

stephendewyer commented 6 months ago

Environment

System: OS: Windows 11 10.0.22631 CPU: (12) x64 13th Gen Intel(R) Core(TM) i7-1355U Memory: 2.92 GB / 15.72 GB Binaries: Node: 20.8.1 - C:\Program Files\nodejs\node.EXE npm: 10.1.0 - C:\Program Files\nodejs\npm.CMD Browsers: Edge: Chromium (123.0.2420.97) Internet Explorer: 11.0.22621.3527 npmPackages: @auth/core: ^0.31.0 => 0.31.0 @auth/sveltekit: ^1.1.0 => 1.1.0

Reproduction URL

https://github.com/stephendewyer/artintechservices/blob/main/src/routes/login-client/%2Bpage.svelte

Describe the issue

I am creating a custom signin page for a project. The signIn function from @auth/sveltekit/client is returning an object that is always status = 200 and ok = true even when the client submits invalid credentials. As a result, I am unable to show users feedback after they submit their credentials. I've tried to find a workaround to no avail. In previous @auth/sveltekit versions (and earlier SvelteKit versions), the signIn function would return status = 400 and ok = false if the credentials submitted by the user were invalid.

How to reproduce

+page.svelte:

<script lang="ts">
    import { PUBLIC_DOMAIN } from "$env/static/public";
    import BannerImage from "$lib/images/Art_in_Tech_Services_banner_with_logo.jpg";
    import { page } from "$app/stores";
    // import PendingFlashMessage from "$lib/components/flashMessages/PendingFlashMessage.svelte";
    // import ErrorFlashMessage from "$lib/components/flashMessages/ErrorFlashMessage.svelte";
    // import SuccessFlashMessage from "$lib/components/flashMessages/SuccessFlashMessage.svelte";
    import EmailInput from "$lib/components/inputs/EmailInput.svelte";
    import PasswordInput from "$lib/components/inputs/PasswordInput.svelte";
    import SubmitButton from "$lib/components/buttons/SubmitButton.svelte";
    import CancelButton from "$lib/components/buttons/CancelButton.svelte";
    import ActionButtonSecondary from "$lib/components/buttons/ActionButtonSecondary.svelte";
    import { goto } from '$app/navigation';
    import { signIn } from "@auth/sveltekit/client";

    // receive form data from server

    let emailInputValue: string = "";
    let emailIsValid: boolean = true;

    let passwordInputValue: string = "";
    let passwordIsValid: boolean = true;

    let loginClientButtonDisabled: boolean = true;

    $: if (
        emailIsValid &&
        passwordIsValid &&
        (passwordInputValue !== "") &&
        (emailInputValue !== "")
    ) {
        loginClientButtonDisabled = false;
    } else {
        loginClientButtonDisabled = true;
    };

    let responseItem: ResponseObj = {
        success: "",
        error: "",
        status: null
    };

    $: if((responseItem.success) || (responseItem.error)) {
        setTimeout(() => {
            responseItem.success = "";
            responseItem.error = "";
            status: null;
        }, 4000)
    };

    const loginClientHandler = async () => {
        pending = true;
        try {
            const response = await signIn("credentials", {
                providerId: "client-login",
                email: emailInputValue,
                password: passwordInputValue,
                redirect: false,
                callbackUrl: "/authenticated-client/client"
            });
            if (response) {
                responseItem.success = "valid email and password";
                emailInputValue = "";
                passwordInputValue = "";
                goto(`/authenticated-client/client`)
            };
        } catch (error) {
            console.log(error);
        };
    };

    // $: console.log($page.data.session?.user?.email)

    let pending: boolean = false;

    $: if((responseItem.success) || (responseItem.error)) {
        pending = false;
    };

</script>

<svelte:head>
    <title>Art in Tech Services - login client</title>
    <meta name="description" content="login client" />
    <meta property="og:image" content={BannerImage} />
    <meta property="og:url" content={PUBLIC_DOMAIN+$page.url.pathname}/>
</svelte:head>

<div class="page">
    <form class="form" on:submit|preventDefault={loginClientHandler}>
        <h1>login client</h1>
        <div class="input_row">
            <EmailInput
                bind:isValid={emailIsValid}
                placeholder="myEmail@myDomain.com"
                inputID="client_email"
                inputName="client_email"
                bind:emailInputValue={emailInputValue}
                inputLabel={true}
                required={true}
            >
                email:
            </EmailInput>
        </div>
        <div class="input_row">
            <PasswordInput
                bind:isValid={passwordIsValid}
                placeholder="myPassword"
                inputID="client_password"
                inputName="client_password"
                inputLabel={true}
                bind:passwordInputValue={passwordInputValue}
                required={true}
                passwordInputErrorMessage="password required"
            >
                password:
            </PasswordInput>
        </div>
        <div class="buttons_container">
            <SubmitButton 
                disable={loginClientButtonDisabled}
            >
                login
            </SubmitButton>
        </div>
    </form>
    <div class="login_helpers_container">
        <div class="login_helpers_column">
            <h4 class="login_helper_prompt">
                don't have an account?
            </h4>
            <a href="/create-a-client-account">
                <ActionButtonSecondary>
                    create a free account
                </ActionButtonSecondary>
            </a>
        </div>
        <div class="login_helpers_column">
            <h4 class="login_helper_prompt">
                forgot your password?
            </h4>
            <a href="/reset-client-password">
                <ActionButtonSecondary>
                    reset password
                </ActionButtonSecondary>
            </a>
        </div>
    </div>
    <a href="/" class="cancel_button_container">
        <CancelButton>
            cancel
        </CancelButton>
    </a>
</div>

auth.js

import { SvelteKitAuth, CredentialsSignin } from "@auth/sveltekit";
import Credentials from "@auth/core/providers/credentials";
import { AUTH_SECRET } from '$env/static/private';
import { clientAuthentication } from "$lib/server/authentication/client-authentication.js";

// class InvalidLoginError extends CredentialsSignin {
//     code = "invalid email and/or password"
// };

export const { handle, signIn, signOut } = SvelteKitAuth({
    providers: [
        Credentials({
            async authorize(credentials) {
                if (credentials.providerId === "client-login") {
                    // @ts-ignore
                    const response = await clientAuthentication(credentials);
                    if (response === null) {
                        return null;
                    } else if (response) {
                        const responseItem = await response;
                        return responseItem ?? null;
                    };
                } else {
                    return null;
                };
            }
        })
    ],
    secret: AUTH_SECRET,
    debug: true,
    session: {
        // maxAge: 1800, // 30 mins
        strategy: "jwt"
    },
    trustHost: true
});

hooks.server.js

import { redirect } from '@sveltejs/kit';
import { handle as authenticationHandle } from './auth';
import { sequence } from "@sveltejs/kit/hooks";

/** @type {import('@sveltejs/kit').Handle} */
const authorizationHandle = async ({event, resolve}) => {
    const session = await event.locals.auth();
    // redirect users to "/authenticated-client/client" if startsWith("/authenticated-client") is false

    if (
        !event.url.pathname.startsWith("/authenticated-client") &&
        session?.user?.name === "client"
    ) {
        throw redirect(303, "/authenticated-client/client");
    };

    // protect any routes under /authenticated-client

    if (event.url.pathname.startsWith("/authenticated-client")) {
        if (session?.user?.name !== "client") {
            throw redirect(303, "/login-client");
        };
    };

    if (
        !event.url.pathname.startsWith("/authenticated-administrator") &&
        session?.user?.name === "administrator"
    ) {
        throw redirect(303, "/authenticated-administrator/administrator");
    };

    if (event.url.pathname.startsWith("/authenticated-administrator")) {
        if (session?.user?.name !== "administrator") {
            throw redirect(303, "/login-administrator");
        };
    };
    // if still request, proceed as normal
    return await resolve(event);
}

export const handle = sequence(
    authenticationHandle,
    authorizationHandle
)

Expected behavior

The signIn function in login-client+page.svelte should return an object with status = 400 and ok = false if the user submits invalid credentials.

roberte777 commented 6 months ago

I am experiencing virtually the exact same issue with the exact same setup, but in the Next.JS codebase.

jeffreys-cat commented 5 months ago

Have the same problem

Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!