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

Cannot store user's session in cookies storage after called "signIn" and "confirmSignIn" successfully. #13725

Open
3 tasks done
cong-tri opened this issue Aug 19, 2024 · 15 comments
Assignees
Labels
Auth Related to Auth components/category pending-community-response Issue is pending a response from the author or community. question General question SSR Issues related to Server Side Rendering

Comments

@cong-tri
Copy link

cong-tri commented Aug 19, 2024

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

Authentication

Amplify Version

v6

Amplify Categories

auth, api

Backend

None

Environment information

z5745521364142_94b6f1b30e358d5a1ae8c6f5311dbd8c

Describe the bug

Cannot store user's session in cookies storage after called Amplify.configure(config, { ssr: true }) and Auth Api "signIn" and "confirmSignIn" successfully, it just stored at localStorage.
I maked it with real domain / IP.
z5745521364142_94b6f1b30e358d5a1ae8c6f5311dbd8c

z5736648592509_0456d5e2af0f137a104fdf58c4273fc0

Expected behavior

I hope it after signIn successful can be stored in cookie storage.

Reproduction steps

  1. npm install aws-amplify , aws-amplify/adapter-nextjs // because i use with nextjs 14
  2. configure amplify in layout.ts file run on server side nextjs:
    const config = getAmplifyConfig(domain); // domain can get from real url domain
    Amplify.configure(config, { ssr: true })
  3. I call Auth api "signIn" and "confirmSignIn" in client side and i transmitted into "signIn" {username, password} and next step would be call "confirmSignIn" transmitted into challegeResponse to handle
  4. If step 3 is successful, it will be created user's session and store at cookies storage. But I have bugs at step 3, i cannot store user's session after handle successfully at cookies, it just store at localStorage.

Code Snippet

const onFinish = async ({
 // Main function
    username,
    password,
  }: {
    username: string;
    password: string;
  }) => {
    const myuuid = uuidv4(); // uuid device
    // console.log(myuuid);

    setUUID(myuuid);

    setAccount({ username, password });

    try {
      const response = await signIn({
        username,
        password,
        options: {
          clientMetadata: {
            uuid: myuuid,
          },
        },
      });
      console.log(response);
      
      if (response.isSignedIn === false) setIsModalOpen(true);
      else {
        message.success("Login Successfully", 2, () => {
          router.refresh();
          router.push("/public-portal/user");
        });
      }
    } catch (error) {
      console.log("Step 1");
      handleThrowErrorMessage(error, errorException);
    }
  };

  const handleReturnOutputSignin = async () => {
    try {
      const output = await signIn({
        username: account?.username ?? "",
        password: account?.password,
        options: {
          clientMetadata: {
            uuid: uuid ?? "",
            mfaMethod: "EMAIL",
          },
        },
      });
      return output;
    } catch (error) {
      handleThrowErrorMessage(error);
    }
  };

  const handleContinueToSignin = async () => {
    try {
      const output = await handleReturnOutputSignin();
      console.log(output);
      
      setOutPut(output);
      if (output?.nextStep.signInStep === "CONFIRM_SIGN_IN_WITH_SMS_CODE")
        setIsModalOpen(true);
    } catch (error) {
      handleThrowErrorMessage(error);
    }
  };

  const handleSignInNextSteps = async ({ otpCode }: { otpCode: string }) => {
    try {
      if (!output) await handleReturnOutputSignin();

      if (
        output?.isSignedIn === false &&
        output?.nextStep.signInStep === "CONFIRM_SIGN_IN_WITH_SMS_CODE"
      ) {
        await confirmSignIn({ challengeResponse: otpCode, options: {
          mfaMethod: "EMAIL",
        } });
        setValid(true);

        message.success("Login Successfully", 2, () => {
          router.refresh();
          router.push("/user");
        });
      }
    } catch (error) {
      handleThrowErrorMessage(error);
    }
  };
  const handleThrowErrorMessage = (error: any, errorKey: string = "") => {
    console.error(error);
    
    if (error instanceof AuthError) {
      if (errorKey != errorException) {
        message.error(error.message);
      }
    }

    if (error.message.includes(errorKey)) {
      setIsModal(true);
    }
  };

Log output

// Put your logs below this line


z5736648592509_0456d5e2af0f137a104fdf58c4273fc0

aws-exports.js

export const getAmplifyConfig = (domain: string): ResourcesConfig => {
  const amplifyConfig: ResourcesConfig = {
    Auth: {
      Cognito: {
        // identityPoolId: process.env.NEXT_PUBLIC_AWS_COGNITO_IDENTITY_POOL_ID ?? '',
        userPoolId: process.env.AWS_COGNITO_USER_POOL_ID ?? "",
        userPoolClientId: process.env.AWS_COGNITO_CLIENT_ID ?? "",
        loginWith: {
          oauth: {
            domain: process.env.AWS_COGNITO_DOMAIN ?? "",
            scopes: [
              "email",
              "openid",
              "profile",
              "aws.cognito.signin.user.admin",
            ],
            redirectSignIn: [`http://${domain}/signin`],
            redirectSignOut: [`http://${domain}/sign-out`],
            responseType: "token",
          },
          email: false,
          username: true,
          phone: false,
        },
      },
    },
  };

  return amplifyConfig;
};

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

@cong-tri cong-tri added the pending-triage Issue is pending triage label Aug 19, 2024
@cwomack cwomack added the Auth Related to Auth components/category label Aug 19, 2024
@cwomack cwomack self-assigned this Aug 19, 2024
@cwomack cwomack added the SSR Issues related to Server Side Rendering label Aug 19, 2024
@cwomack
Copy link
Member

cwomack commented Aug 19, 2024

Hello, @cong-tri and thanks for opening this issue. Can you clarify what you mean by you, "transmitted into 'signIn' {username, password} and next step would be call "confirmSignIn" transmitted into challegeResponse to handle"? And to better understand how that second error you're seeing tied to the [DUPLICATED_DEVICE], can you give some details on how the Preauthentication trigger hook is implemented? I'm not able to find where that error is coming from on the Amplify/Cognito side.

@cwomack cwomack added question General question pending-response and removed pending-triage Issue is pending triage labels Aug 19, 2024
@cong-tri
Copy link
Author

That error event was created by our company to handle the error of duplicate device id when logging in from 2 or more devices, only logged in on 1 device. That passed successfully and logged in successfully. And the only bug left is that I can't save the user's session in cookies so I can check if there is a session or not in the middleware with Nextjs that gets it from the cookies.

@cong-tri
Copy link
Author

and the parameter "errorException" is "[DUPLICATED_DEVICE]".

@cwomack
Copy link
Member

cwomack commented Aug 20, 2024

@cong-tri, can you provide a screenshot or more details from the network tab logs of what's happening when these errors are happening? You should be able to go to the network tab in your dev tools and attach the har logs? More info here), but word of caution that we'd advise you to open the file and remove any sensitive data before posting it publicly in a Github issue here.

@israx
Copy link
Member

israx commented Aug 21, 2024

hello @cong-tri. The UnexpectedSignInInterruption exception typically occurs when the user's cookies are rejected by the browser. This can be due to an invalid domain. For instance, if you are authenticating via localhost:3000, but your cookies are configured with a production domain, then the browser will reject them.

Here are some helpful considerations for resolving the issue.

  1. Console log window.location.origin and see what is the current domain.
  2. Configure your cookies with the domain that is authenticating.

Also, are you experiencing this issue on Chrome, Firefox and Safari and during local development only?

@cong-tri
Copy link
Author

thanks for your advice. I will try it.

@cong-tri
Copy link
Author

i had tried to configure AWS Amplify just one time to work both on the server side (using Next.js) and on the client side. In my setup, i extracted the real public IP or domain from a variable named "host" in a function called header() within your layout.ts file on server side. This value could either be an internal IP address like "http://192.168.0.xxx:xxxx/xxx" for non-secure connections or a public-facing domain like "https://dev.xxxxx.com:xxxx/" for secure connections. Can be i made a mistake in the way to config amplify aws?

@cong-tri
Copy link
Author

cong-tri commented Aug 30, 2024

I had config with {ssr = true} 3 files: server component, client file, server file to call "runWithAmplifyServerContext".

  • Server component layout.ts :
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const headersList = headers();
  const domain: string = headersList.get("host") ?? "";

  const config = getAmplifyConfig(domain);
  Amplify.configure(config, { ssr: true });
  
  return (
    <html lang="en">
      <body className={inter.className}>
                <ConfigureAmplifyClientSide domain={domain}>
                    <main className="main">
                    {children}
                    </main>
                </ConfigureAmplifyClientSide>
              {/* </ReactQueryClientProvider> */}
      </body>
    </html>
  );
}
  • client side ConfigureAmplifyClientSide component :
"use client";
import React from "react";
import { Amplify } from "aws-amplify";
import { getAmplifyConfig } from "../../amplify-config";

const ConfigureAmplifyClientSide = ({
  children,
  domain,
}: {
  children: React.ReactNode;
  domain: string;
}) => {
  const config = getAmplifyConfig(domain ?? "");
  Amplify.configure(config, { ssr: true });
  return <>{children}</>;
};
export default ConfigureAmplifyClientSide;
+ Server file amplify_server.ts: // to call in middleware.ts
import { createServerRunner } from "@aws-amplify/adapter-nextjs";
import { getAmplifyConfig } from "../../amplify-config";

export const createServerAmplify = (domain: string) => {
  const config = getAmplifyConfig(domain ?? "");
  const { runWithAmplifyServerContext } = createServerRunner({
    config: config,
  });
  return { runWithAmplifyServerContext };
};

I had the bug: confirmSignIn could not auto access user session after call signIn and enable ssr = true, but not enable ssr = true could be successful and store anything else data of user at localStorage.

@cong-tri
Copy link
Author

"getAmplifyConfig.ts" file :
export const getAmplifyConfig = (domain: string): ResourcesConfig => {
const amplifyConfig: ResourcesConfig = {
Auth: {
Cognito: {
// identityPoolId: process.env.NEXT_PUBLIC_AWS_COGNITO_IDENTITY_POOL_ID ?? '',
userPoolId: process.env.AWS_COGNITO_USER_POOL_ID ?? "",
userPoolClientId: process.env.AWS_COGNITO_CLIENT_ID ?? "",
loginWith: {
oauth: {
domain: process.env.AWS_COGNITO_DOMAIN ?? "",
scopes: [
"email",
"openid",
"profile",
"aws.cognito.signin.user.admin",
],
redirectSignIn: [http://${domain}/signin],
redirectSignOut: [http://${domain}/sign-out],
responseType: "token",
},
email: false,
username: true,
phone: false,
},
},
}

@cong-tri
Copy link
Author

cong-tri commented Aug 30, 2024

middleware.ts :

import { headers } from "next/headers";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

import { createServerAmplify } from "./utils/amplify_server";
import { fetchAuthSession } from "aws-amplify/auth/server";

// This function can be marked `async` if using `await` inside
export async function middleware(request: NextRequest) {
  const response = NextResponse.next();
  const headersList = headers();

  const domain: string = headersList.get("host") ?? "";

  const { runWithAmplifyServerContext } = createServerAmplify(domain);

  const authenticated = await runWithAmplifyServerContext({
    nextServerContext: { request, response },
    operation: async (contextSpec) => {
      try {
        const session = await fetchAuthSession(contextSpec);

        return !!session?.tokens?.idToken;
      } catch (error) {
        console.error(error);
      }
      return false;
    },
  });

  if (config.matcher.includes(request.nextUrl.pathname) && !authenticated) {
    return NextResponse.redirect(new URL("/xxx/signin", request.url));
  }

  return response;
}

// See "Matching Paths" below to learn more
export const config = {
  matcher: ["/xxx", "/xxx"],
};

@cwomack
Copy link
Member

cwomack commented Aug 30, 2024

@cong-tri, I think there's potentially a couple things we can do to isolate the issue here after looking through your above provided code.

  1. It looks like you're calling Amplify.configure() on the server side (within your Server component layout.ts). Can you remove that and see if it changes the behavior and this error?
  2. Can you help us understand why you're trying to dynamically change the config based on the domains? Can you remove this logic and see if it resolves the errors (as well as the above removal of the extra Amplify.configure call). Your line of const config = getAmplifyConfig(domain); might be prone to errors when trying to swap the config at run time, and we recommend only trying to set the config at build time.

If you're still experiencing the errors with signIn() and confirmSignIn() after doing the above, we should be able to better find the root cause of the problems here. Thanks!

@cong-tri
Copy link
Author

cong-tri commented Sep 6, 2024

Because i don't want to manual config, i want to config can be re-used. But i try to use manual config but it still cannot work for me. And i removed Amplify.configure(config, {ssr: true}) in server component layout.tsx.
amplify-configure.json :
{
"Auth": {
"Cognito": {
"region": "ap-xxxx-1",
"userPoolClientId": "xxxxxxxxxxxxxxxxxxx",
"userPoolId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"loginWith": {
"oauth": {
"domain": "https://cognito-idp.ap-xxxx-1.amazonaws.com/ap-xxxx-1_xxxxx",
"scopes": [
"openid",
"email",
"phone",
"profile",
"aws.cognito.signin.user.admin"
],
"redirectSignIn": ["http://192.168.0.xxx:xxxx/public-portal/signin"],
"redirectSignOut": ["http://192.168.0.xxx:xxxx/public-portal/signout"],
"responseType": "token"
},
"username": "true",
"email": "false",
"phone": "false"
}
}
}
}

@github-actions github-actions bot added the pending-maintainer-response Issue is pending a response from the Amplify team. label Sep 6, 2024
@israx
Copy link
Member

israx commented Sep 6, 2024

Hello @cong-tri. The problem lies in the browser rejecting the cookies due to a domain mismatch or the secure flag enabled for local development.

Let's try configuring Amplify on the client as showed below,

"use client";
import React from "react";
import { Amplify } from "aws-amplify";
import { CookieStorage } from 'aws-amplify/utils';
import { cognitoUserPoolsTokenProvider } from 'aws-amplify/auth/cognito';
import { getAmplifyConfig } from "../../amplify-config";

const ConfigureAmplifyClientSide = ({
  children,
  domain,
}: {
  children: React.ReactNode;
  domain: string;
}) => {
  const config = getAmplifyConfig(domain ?? "");
  Amplify.configure(config, { ssr: true });

  cognitoUserPoolsTokenProvider.setKeyValueStorage(new CookieStorage(
   secure: !isLocalDevelopment() // If it is local development then the secure flag should be false, otherwise true.
  ));
  return <>{children}</>;
};
export default ConfigureAmplifyClientSide;

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending a response from the Amplify team. label Sep 6, 2024
@cwomack cwomack added pending-community-response Issue is pending a response from the author or community. and removed pending-response labels Sep 10, 2024
@cong-tri
Copy link
Author

cong-tri commented Sep 12, 2024

thank @israx, @cwomack, @robbevan for give me advice. I recognize my main bug is that have not yet set up domain for cookie. I will try it. Thanks.
i had tried it and not work:

  • i created one file manual amplify-config.ts
  • i created again a file client component to configure amplify:
    "use client";

import { Amplify } from "aws-amplify";
import { cognitoUserPoolsTokenProvider } from "aws-amplify/auth/cognito";
import { CookieStorage } from "aws-amplify/utils";
import { getAmplifyConfig } from "../../amplify-config";
const config = getAmplifyConfig();
/* i had fix manual config with domain string, domain = "http:/192.168.x.xxxx"
loginWith: {
oauth: {
domain: process.env.AWS_COGNITO_DOMAIN ?? "",
scopes: [
"email",
"openid",
"profile",
"aws.cognito.signin.user.admin",
],
redirectSignIn: [http://${domain}/signin],
redirectSignOut: [http://${domain}/sign-out],
responseType: "token",
},
*/
Amplify.configure(config, {
ssr: true,
});

cognitoUserPoolsTokenProvider.setKeyValueStorage(
new CookieStorage({
domain: "http:/192.168.x.xxxx",
path: "/public-portal",
secure: false,
sameSite: "strict",
})
);

export default function ConfigureAmplifyClientSide() {
return null;
}

@github-actions github-actions bot added pending-maintainer-response Issue is pending a response from the Amplify team. and removed pending-community-response Issue is pending a response from the author or community. labels Sep 12, 2024
@cwomack
Copy link
Member

cwomack commented Oct 8, 2024

@cong-tri are you still experiencing this? Just want to make sure I understood the comment above that you attempted to resolve this per @israx's advice in this comment and you're still experiencing an issue?

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending a response from the Amplify team. label Oct 8, 2024
@cwomack cwomack added the pending-community-response Issue is pending a response from the author or community. label Oct 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auth Related to Auth components/category pending-community-response Issue is pending a response from the author or community. question General question SSR Issues related to Server Side Rendering
Projects
None yet
Development

No branches or pull requests

3 participants