aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.42k stars 2.12k forks source link

RFC: Amplify JS Library v6 Developer Preview - Smaller Bundle Size, Improved TypeScript and Next.js support #12147

Closed renebrandel closed 10 months ago

renebrandel commented 11 months ago

This RFC outlines the changes coming in the Amplify Library v6 developer preview. The v6 launch will focus on 3 key areas:

  1. Smaller bundle size (thanks for contributing to #10727)
  2. Improved TypeScript support (thanks for contributing to #11113)
  3. Improved Next.js support, including App Router, API Routes, middleware, and more.

We'd love to use this RFC to collect your feedback as we rollout new functionalities to this preview before general availability.

Note: Moving from v5 to v6 is a breaking change. Follow the migration guide on the bottom of this comment to learn more. As of the time of writing, v6 is still in developer preview and should not be used for production workloads.

New enhancements

Improved Bundle Sizes

Enhanced TypeScript Support

Improved Next.js support (App Router, API routes, middleware)

Provide Feedback

We welcome your input on the proposed changes! Please try the Amplify Library v6 developer preview and share feedback by commenting on this RFC.

How to get started

To get started install the v6 preview of Amplify JS using the @next tag:

npm install aws-amplify@next

For a step-by-step walkthrough to use Amplify JS v6 preview in your Next.js, React, Vue.js, or Angular.js app, check out our documentation.

How to migrate from v5 -> v6 Developer Preview

You can review the full migration guide on our v6 (Preview) documentation. The primary steps are summarized below:

To upgrade to the v6 developer preview. Install the @next tagged version of Amplify JS:

npm install aws-amplify@next

Instead of importing specific classes from a parent package, you must now import the specific functionalities from subpaths:

Before in v5

import { Amplify, Auth } from 'aws-amplify'
import awsconfig from './aws-exports'

Amplify.configure(awsconfig)

...
Auth.signIn(...)

Now in v6

import { Amplify } from 'aws-amplify'
import { signIn } from 'aws-amplify/auth'
import awsconfig from './aws-exports'

Amplify.configure(awsconfig)

...
signIn(...)
MyNameIsTakenOMG commented 11 months ago

Oh, finally we don't have to import Amplify.configure in multiple places😂 And amplify category APIs look great! Cannot wait for v6 to come out👍

jselsing commented 11 months ago

Do you have an example app based on the Next.js App Router somewhere that can be linked to? I would be great to see a reference implementation. Maybe I missed it in the docs.

I'm getting "aws-amplify@next is not in the npm registry, or you have no permission to fetch it." for the initial package install. Does anyone else experience the same?

nadetastic commented 11 months ago

Hi @jselsing I'm able to run npm install aws-amplify@next with no issues on my end. Also here is the current link to it in npm - https://www.npmjs.com/package/aws-amplify/v/6.0.1-next.a1ea0f2.0

For an example with Next.js App Router, have you had a chance to take a look at this section of the V6 preview documentation?

CGarces commented 11 months ago

Where is de api module? there is no API REST section on the new documentation.

louisthomaspro commented 11 months ago

Hello team, thanks for being focussed on this important package! I am so excited to try it. I faced few issues when implementing this on Next.js App router:

  1. Types are broken in runWithAmplifyServerContext in middleware.ts

    Type 'NextRequest' is not assignable to type 'IncomingMessage & { cookies: Partial<{ [key: string]: string; }>; }'

    image

  2. When running the app I have a module not found error

    ./node_modules/.pnpm/@aws-amplify+adapter-nextjs@0.0.2-next.a1ea0f2.0_aws-amplify@6.0.1-next.a1ea0f2.0_next@13.5.3/node_modules/@aws-amplify/adapter-nextjs/lib-esm/utils/createCookieStorageAdapterFromNextServerContext.js:15:0
    Module not found: Can't resolve '@aws-amplify/core/internals/adapter-core'

Moreover, how can I configure the "sameSite" cookies with the new configuration interface ?

cookieStorage: {
    domain: process.env.BASE_URL?.replace(/https?:\/\//, "") || "",
    path: "/",
    secure: process.env.NODE_ENV === "production",
    sameSite: "lax", 
},

For SignIn, is there an equivalent to:

autoSignIn: {
    enabled: true,
},

Thanks!

cwomack commented 11 months ago

@CGarces the API REST section is not part of the developer preview, but will have updated documentation and improvements when we move v6 to General Availability. This developer preview was only for a few categories of Amplify.

jselsing commented 11 months ago

Hi @jselsing I'm able to run npm install aws-amplify@next with no issues on my end. Also here is the current link to it in npm - https://www.npmjs.com/package/aws-amplify/v/6.0.1-next.a1ea0f2.0

For an example with Next.js App Router, have you had a chance to take a look at this section of the V6 preview documentation?

Thanks for the reply. Fully specifying the rpm link solved the issue for me. I had to do "npm i aws-amplify@6.0.1-next.a1ea0f2.0".

Yes, I read through the documentation, but I cannot get the configuration to run. Do you have an example amplifyconfiguration.json where is works for you, or even better, documentation for doing dynamic configuration where resource values are read from environment variables.

didemkkaslan commented 11 months ago

Hello team, thanks for being focussed on this important package! I am so excited to try it. I faced few issues when implementing this on Next.js App router:

  1. Types are broken in runWithAmplifyServerContext in middleware.ts
Type 'NextRequest' is not assignable to type 'IncomingMessage & { cookies: Partial<{ [key: string]: string; }>; }'

image

  1. When running the app I have a module not found error
./node_modules/.pnpm/@aws-amplify+adapter-nextjs@0.0.2-next.a1ea0f2.0_aws-amplify@6.0.1-next.a1ea0f2.0_next@13.5.3/node_modules/@aws-amplify/adapter-nextjs/lib-esm/utils/createCookieStorageAdapterFromNextServerContext.js:15:0
Module not found: Can't resolve '@aws-amplify/core/internals/adapter-core'

Moreover, how can I configure the "sameSite" cookies with the new configuration interface ?

cookieStorage: {
    domain: process.env.BASE_URL?.replace(/https?:\/\//, "") || "",
    path: "/",
    secure: process.env.NODE_ENV === "production",
    sameSite: "lax", 
},

For SignIn, is there an equivalent to:

autoSignIn: {
    enabled: true,
},

Thanks!

Same for me. We really need an example repo I'm quite confused

nadetastic commented 11 months ago

Hi @louisthomaspro @didemkkaslan can you share the full middleware.ts file you have setup? Also can you share the following?

I'm working on setting up a sample app that works and will share that soon?

didemkkaslan commented 11 months ago

Hi @louisthomaspro @didemkkaslan can you share the full middleware.ts file you have setup? Also can you share the following?

  • version of next.js
  • version of amplify cli
  • are you using npm, yarn, pnpm?

I'm working on setting up a sample app that works and will share that soon?

Hello thanks for the quick reply :) I already had an app configured with amplify so I just installed the new packages @aws-amplify/adapter-nextjs and aws-amplify@latest . I didn't do the amplify init phase since we already done it long ago. Trying to use the new app router with middleware auth checks so I might be doing some wrong stuff

Btw thank you will be waiting for the sample app

NextJS version: 13.5.1 Amplify cli: 12.5.0 Package manager: yarn

middleware.ts

import { runWithAmplifyServerContext } from '@aws-amplify/adapter-nextjs';
import { fetchAuthSession } from 'aws-amplify/auth/server';
import { NextRequest, NextResponse } from 'next/server';

export default withAuth(
  async function middleware(request: NextRequest, response: NextResponse) {
    const authenticated = await runWithAmplifyServerContext({
      nextServerContext: { request, response },
      operation: async (contextSpec) => {
        try {
          const session = await fetchAuthSession(contextSpec, {});
          return session.tokens !== undefined;
        } catch (error) {
          console.log(error);
          return false;
        }
      },
    });
    const isAuthPage =
      request.nextUrl.pathname.startsWith("/login") ||
      request.nextUrl.pathname.startsWith("/register")

    if (isAuthPage) {
      if (authenticated) {
        return NextResponse.redirect(new URL("/dashboard", request.url))
      }

      return NextResponse.next();
    }

      return NextResponse.redirect(
        new URL(`/login`, request.url)
      );

  },
)

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
}
nadetastic commented 11 months ago

Thanks for confirming and sharing @didemkkaslan - quick note, you will need to install with aws-amplify@next and not @latest which is still v5. Also you shouldn't need to change your backed configuration (amplify add/init ...) etc however you will also need to upgrade your Amplify CLI version to 12.5.1 or higher as well

nadetastic commented 11 months ago

Hi all, cc @didemkkaslan @jselsing

following up here: I have setup a working Next/SSR app that uses Amplify Auth here - https://github.com/nadetastic/amplify-v6-ssr-dev-preview

The main files to look at that allow this to work:

Also note the version numbers for aws-amplify and @aws-amplify/adapter-nextjs dependencies.

mattiLeBlanc commented 11 months ago

HI, I was trying out the new v6 Amplify, but I am running in some migration issues: I was using

import { GraphQLResult, GRAPHQL_AUTH_MODE } from '@aws-amplify/api';
import { AWSAppSyncRealTimeProvider } from '@aws-amplify/pubsub';
import { Auth, API } from 'aws-amplify';

and calls like

const currentUser = await Auth.currentUserInfo();

which can be replaced by (i tink)

const session = await fetchAuthSession()
  ... 
return {
   'Authorization': session.tokens.idToken
 }

and graphql calls like:

const response = API.graphql(payload) as Promise<GraphQLResult<object>>

Also I we had to use:

import { CognitoUserSession, CognitoUser } from 'amazon-cognito-identity-js';

to get our typings for Cognito user and session Will this be supported in a separate module?

didemkkaslan commented 11 months ago

Hi all, cc @didemkkaslan @jselsing

following up here: I have setup a working Next/SSR app that uses Amplify Auth here - https://github.com/nadetastic/amplify-v6-ssr-dev-preview

The main files to look at that allow this to work:

  • next.config.js (as shown in documentation)
  • middleware.ts (as shown in documentation)
  • components/ConfigureAmplifyClientSide.ts and app/layout.tsx (as shown in documentation)
  • src/amplifyconfiguration.sample.json (copy of config file that works)

Also note the version numbers for aws-amplify and @aws-amplify/adapter-nextjs dependencies.

Amazing thank you 💯 🥳 I've upgraded my cli version to 12.6.0 and with that example repo works for me.

But for my actual project since it has a dynamic configuration with env variables there is no API field in the Amplify.configure so couldn't really test it. Will be waiting for the API

Screenshot 2023-10-06 at 08 45 07 Screenshot 2023-10-06 at 08 46 01
nadetastic commented 11 months ago

Hi @mattiLeBlanc - the changes from V5 to V6 will also see CognitoUser going away. With that said the typings for Auth will mainly be around the Input and Output of the APIs, ex SignUpInput and SignUpOutput.

The types should then be imported from aws-amplify/auth

import {signUp, SignUpInput, SignUpOutput} from 'aws-amplify/auth'

Also keep in mind as you test this ou, using multiple version of amplify may cause other errors.

mattiLeBlanc commented 11 months ago

@nadetastic I do rely on functions like

Auth.currentAuthenticatedUser()
Auith.signIn()
Auth.currentSession()
Auth.currentUserInfo()
Auth.signOut()

Are those going to be available? I need to be able to resolve the current user on page refresh, so that my Angular resolvers can fetch the appropriate data based on the Cognito Groups assigned. And I need to be able to extract the custom attributes which I use.

Do you want me to try an upgrade again in a separate branch and see if I can get my app to work with what is available and tell what could be missing?

nadetastic commented 11 months ago

@mattiLeBlanc the actions around these methods will still be available, but the method name may be different. For example, Auth.currentAuthenticatedUser() becomes getCurrentUser() and the output of this will be typed.

mattiLeBlanc commented 11 months ago

@nadetastic Wonderful. Thank for the great work, team!

matamatanot commented 11 months ago

Improved Next.js support (App Router, API routes, middleware)

I understand that App Router, API routes and middleware will be supported, but is the runtime Node.js? Next.js (+ Vercel) usually defaults to edge runtime (Cloudflare workers) as the these environments.

Can a lightweight JavaScript environment with winterCG compliant webAPI available be used for AWS in the future without relying on the Node.js api?

For example, Auth0(nextjs-auth0) has updated to release a module that can be used with Edge Runtime.

ref https://wintercg.org/ https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes https://github.com/auth0/nextjs-auth0/pull/1269

HuiSF commented 11 months ago

Hi @matamatanot thanks for the information and inquiry!

According to Next.js documentation, the default runtime that a Next.js app runs on will be nodejs, e.g., in the Next.js documentation you linked:

The Node.js Runtime (default) has access to all Node.js APIs and compatible packages from the ecosystem.

Also in the Route Segment Config, the default runtime option is nodejs.

When deploying a Next.js app to Vercel, unless you change the aforementioned option, only the middleware will be running on the EdgeRuntime by default. With v6 developer preview, we guarantee the Auth APIs can be used on EdgeRuntime, details see: https://docs.amplify.aws/lib-v1/ssr/nextjs/q/platform/js/#amplify-api-nextjs-server-support

Currently, we are focusing on delivering support for the default options of Next.js with a robust developer experience, and we will look into EdgeRuntime compatibility for more Amplify library APIs in the foreseeable future.

matamatanot commented 11 months ago

@HuiSF Thanks for the quick reply.

the default runtime that a Next.js app runs on will be nodejs only the middleware will be running on the EdgeRuntime by default.

I apologize for the misunderstanding.

EdgeRuntime compatibility for more Amplify library APIs in the foreseeable future.

Very exciting. Can't wait!

Meags27 commented 11 months ago

Edit: Solved, but left comments here for others:

Using App Router with Next.js and just migrating to v6 now.

I have this error "Error: Cannot find module '@aws-amplify/adapter-nextjs/with-amplify'" where I'm following https://docs.amplify.aws/lib-v1/ssr/nextjs/q/platform/js/#configure-amplify-for-server-side-usage

And trying to setup the next.config.js file const { withAmplify } = require('@aws-amplify/adapter-nextjs/with-amplify');

Edit: I tried adding "@aws-amplify/adapter-nextjs": "^0.0.2-next.a1ea0f2.0", (And using Also using "aws-amplify": "^6.0.1-next.a1ea0f2.0",) as shown in the example repo up there, but the error is

"npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: next-site@0.1.0 npm ERR! Found: aws-amplify@6.0.1-ui-preview.1aa6ecb.0 npm ERR! node_modules/aws-amplify npm ERR! aws-amplify@"^6.0.1-next.a1ea0f2.0" from the root project npm ERR! npm ERR! Could not resolve dependency: npm ERR! peer aws-amplify@"^6.0.0" from @aws-amplify/adapter-nextjs@0.0.2-ui-preview.1aa6e cb.0 npm ERR! node_modules/@aws-amplify/adapter-nextjs npm ERR! @aws-amplify/adapter-nextjs@"^0.0.2-next.a1ea0f2.0" from the root project "

Edit: Solved by installing it exactly like this: npm install @aws-amplify/adapter-nextjs@^0.0.2-next.a1ea0f2.0 instead of what I tried which was "npm install @aws-amplify/adapter-nextjs"

Meags27 commented 11 months ago

Has anyone been able to get signInWithRedirect() working with the Hosted UI for Cognito? For me, when it redirects back to the site, it just gets stuck at the /success page with code=abc&state=def (e.g) so the user never gets authenticated or gets tokens.

I notice when I go to the browser console, that signInWithRedirect() gets called properly on my /login route but when redirecting back to my callback uri /success?code=abc&state=def, it only has refreshTokens() called, and nothing happens. This flow doesn't seem to trigger which is needed: urlListener is suppose to call parseRedirectURL, which calls handleAuthResponse, which calls handleCodeFlow , which handles extracting the code from the url, exchanging for tokens and removing the temporary local storage cognito tokens. But none of that calls when the user gets redirected from the Hosted UI.

I also tried subscribing to the hub for signInWithRedirect() errors but nothing gets called (not sure if I have that working though but I followed the doc)

Can someone help here?

I also have the same question as @didemkkaslan how do you set cookie storage for tokens?

cookieStorage: { domain: "localhost", secure: false, // Set to true if using https path: '/', sameSite: "strict", expires: 365, // Cookie expiry in days },

mattiLeBlanc commented 11 months ago

Yes, I think we should still be able to use Cookie storage , I hope that will be made possible?

louisthomaspro commented 11 months ago

I was able to setup the new Amplify V6 on a NextJS App router project, it's working like a charm thanks!

I noticed a slow down on each page or server actions/APIs calls because it fetched the JWK to verify the token.

POST https://cognito-identity.eu-west-1.amazonaws.com/ 200 in 1711ms (cache: SKIP)
 │  │  Cache missed reason: (cache: no-store)

To improve UX, I removed the token verification in middleware and I am doing it at page.tsx level to show a nice loading.tsx.

Suggestion:

Is it possible to control the cache of this fetch as per the best pratice of this documentation ? Or provide this JWK ourself in the fetchAuthSession method ? The fetch could be https://cognito-idp.<Region>.amazonaws.com/<userPoolId>/.well-known/jwks.json?kid=<tokenKid> It will be cached till the kid of the token change.

Meags27 commented 11 months ago

@nadetastic I got an email that you replied, but I can't see if here for some reason. Was it deleted because you ended up having issues with signInWithRedirect()?

In response to your comment, there are no errors. I am using next.js, SST, typescript, tailwind and the amplify.js library.

In the layout.tsx I have

Amplify.configure(awsAmplifyConfig, {
  ssr: true
});

I configure it client side with <ConfigureAmplifyClientSide /> inside the body

When they go to the /login page, I return <button onClick={() => signInWithRedirect()}>Open Hosted UI

User gets redirected to the hosted ui, puts in their email, password, mfa, gets redirected to /success and I leave them there for testing but nothing happens.

I wanted to be able to breakpoint Amplify source code to see what's happening, but I couldn't figure out how. Do you have any suggestions there?

Because "inflightOAuth, oauthPKCE and oauthState" doesn't get deleted from local storage properly at /success, if I try and click the button to signInWithRedirect() again at /login after I've tried once, then signInWithRedirect() will not redirect, so I manually delete those and then I can test again.

The network requests after signing in with hosted UI and ending up back at /success are:

  1. 302 Found https://redacted.auth.us-west-2.amazoncognito.com/mfa?redirect_uri=redacted
  2. 200 OK http://localhost:3000/success?code=redacted&state=redacted
  3. layout.css
  4. webpack
  5. main-app.js
  6. error.js
  7. layout.js
  8. page.js

When I look at the http://localhost:3000/success?... sources in the browser dev tools it has this folder structure: node_modules --> @ aws-amplify --> auth-lib-esm --> providers/cognito --> apis and then tokenRefresher.js And also a folder for credentialsProvider, tokenProvider, utils

But signInWithRedirect.js is not in the sources or run on that page it seems.

Does your test have signInWithRedirect.js in the request?

Let me know if you have anything else I could try and test.

VIEWVIEWVIEW commented 11 months ago

amplify-aws@next breaks compatibility with Vite SSR and therefore SvelteKit and many others. The module is interpreted as commonjs, therefore the export keywords breaks.

Publint gives some hints on how to explicitly mark it as ESM, although I haven't tried these changes yet: https://publint.dev/aws-amplify@6.0.1-next.a1ea0f2.0

Error which is produced when trying to use Amplify's Auth in a SvelteKit server route:

(node:28548) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
1:35:00 PM [vite] Error when evaluating SSR module /src/routes/auth/signup/+page.server.ts: failed to import "aws-amplify"
|- \node_modules\aws-amplify\lib-esm\index.js:6
export { DefaultAmplify as Amplify } from './initSingleton';
^^^^^^

SyntaxError: Unexpected token 'export'
    at internalCompileFunction (node:internal/vm:73:18)
    at wrapSafe (node:internal/modules/cjs/loader:1153:20)
    at Module._compile (node:internal/modules/cjs/loader:1205:27)
    at Module._extensions..js (node:internal/modules/cjs/loader:1295:10)
    at Module.load (node:internal/modules/cjs/loader:1091:32)
    at Module._load (node:internal/modules/cjs/loader:938:12)
    at cjsLoader (node:internal/modules/esm/translators:284:17)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:234:7)
    at ModuleJob.run (node:internal/modules/esm/module_job:217:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)

\node_modules\aws-amplify\lib-esm\index.js:6
export { DefaultAmplify as Amplify } from './initSingleton';
^^^^^^

SyntaxError: Unexpected token 'export'
    at internalCompileFunction (node:internal/vm:73:18)
    at wrapSafe (node:internal/modules/cjs/loader:1153:20)
^^^^^^

SyntaxError: Unexpected token 'export'
    at internalCompileFunction (node:internal/vm:73:18)
    at wrapSafe (node:internal/modules/cjs/loader:1153:20)
    at Module._compile (node:internal/modules/cjs/loader:1205:27)
    at Module._extensions..js (node:internal/modules/cjs/loader:1295:10)
    at Module.load (node:internal/modules/cjs/loader:1091:32)
    at Module._load (node:internal/modules/cjs/loader:938:12)
    at cjsLoader (node:internal/modules/esm/translators:284:17)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:234:7)
    at ModuleJob.run (node:internal/modules/esm/module_job:217:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)
nadetastic commented 11 months ago

@Meags27 There was some confusion between your comment and this issue and wanted to discuss with the team. For now it seems that the comment and the issue are unrelated - I haven't had any issue with signInWithRedirect() so far, but it looks like your issue is specific to SSR, i'm currently testing this and will follow up shortly.

Meags27 commented 11 months ago

@nadetastic Ah yes, that is an unrelated feature request, sorry for the confusion. I'm not sure if the issue is related to server side rendering. My /login route is a server page with a client component button for signInWithRedirect() and my /success page is a server page but only renders a client component too.

There's no errors which makes it hard to debug

This is my amplifyConfig file, which works as I get redirected to the proper sign in page, just nothing happens on the /success page.

import {LegacyConfig, ResourcesConfig} from "@aws-amplify/core/lib/singleton/types";

const awsAmplifyConfig: ResourcesConfig | LegacyConfig = {
    Auth: {
        Cognito:  {
            userPoolClientId: process.env.NEXT_PUBLIC_USER_POOL_CLIENT_ID || "",
            userPoolId: process.env.NEXT_PUBLIC_USER_POOL_ID || "",
            identityPoolId: "",
            signUpVerificationMethod: "code",
            loginWith: {
                oauth: {
                    domain: process.env.NEXT_PUBLIC_COGNITO_DOMAIN + ".auth.us-west-2.amazoncognito.com",
                    scopes: [
                        "email",
                        "profile",
                        "openid",
                        "aws.cognito.signin.user.admin",
                    ],
                    redirectSignIn: ["http://localhost:3000/success"],                   
                    redirectSignOut: ["http://localhost:3000/"],
                    responseType: "code",           
                }
            }
        },
    },   
};
export default awsAmplifyConfig;
nadetastic commented 11 months ago

Hi @VIEWVIEWVIEW thanks for your feedback on the dev preview! I'm curious about the error that you are seeing and would like to understand how you are using aws-amplify@next. You mentioned that it breaks compatibility with Vite SSR and SvelteKit - are you using the library on the server side? Are you able to share a code snippet or context on how this is happening?

VIEWVIEWVIEW commented 11 months ago

Hi @VIEWVIEWVIEW thanks for your feedback on the dev preview! I'm curious about the error that you are seeing and would like to understand how you are using aws-amplify@next. You mentioned that it breaks compatibility with Vite SSR and SvelteKit - are you using the library on the server side? Are you able to share a code snippet or context on how this is happening?

Yes, I use the library on the server side.

I've created a small example here: https://stackblitz.com/edit/sveltejs-kit-template-default-4yb3p5?file=src%2Froutes%2F%2Bpage.server.js

nadetastic commented 11 months ago

@Meags27 thank you for the detailed steps and context! I have been able to reproduced this issue and are discussing with the team.

Some additional context on my findings - It seems that this issue is specific to two things:

Due to the above, a potential work around for you is to have the redirectSignIn url go to the root of you app, then within your app, you can redirect to the success route based on the authentication state. In the meantime let me know if the work around works for you or have any additional questions or issues.

Meags27 commented 11 months ago

@nadetastic Yep you got it, it successfully authenticates the user and gives them auth tokens when I switch it to "/" and not "/success"

I was curious if this part from handleCodeFlow() might've been the issue as it just returns and doesn't throw an error and looks like it is. It mustn't be getting the path name properly.

const currentUrlPathname = url.pathname || '/';
const redirectUriPathname = new URL(redirectUri).pathname || '/';

if (!code || currentUrlPathname !== redirectUriPathname) {
    return;
}

For the meantime, I'll use your suggestion to route to /success after login and hopefully it can get fixed at some point as well.

I really like V6, the middleware works perfectly and typescript support makes coding much easier.

Meags27 commented 11 months ago

Edit: Fixed (below), just leaving this here for others.

@nadetastic In V6, how do you determine on a server component if a user is logged in or not, and don't want to throw an error if they're not logged in?

I'm conditionally rendering the navigation bar based on if they're logged in or not with <AuthNavBar /> and <NotAuthNavBar />

However when I use this below to check if a user is authenticated on the server, it shows undefined, even though the user is now logged in with cookies/tokens stored. Passing in {forceRefresh: true} still shows "undefined".

import {fetchAuthSession} from "aws-amplify/auth";
...
user = await fetchAuthSession();
//returns undefined tokens, credentials, identityId and userSub if not authenticated
authenticated =  user.tokens === undefined ? false : true;
console.log(user)
...
{
  tokens: undefined,
  credentials: undefined,
  identityId: undefined,
  userSub: undefined
}
Meags27 commented 11 months ago

@nadetastic Nevermind, I figured out you have to use import {fetchAuthSession} from '@aws-amplify/auth/server'; instead of import {fetchAuthSession} from '@aws-amplify/auth';

And I call it like this

      user = await runWithAmplifyServerContext({
            nextServerContext: { cookies },
            //operation: (contextSpec) => getCurrentUser(contextSpec),
//operation: (contextSpec) => fetchUserAttributes(contextSpec),
            operation: (contextSpec) => fetchAuthSession(contextSpec),
        });

Only thing I noticed is calling operation: (contextSpec) => fetchUserAttributes(contextSpec) runs this type error printed on the server console TypeError: Cannot read properties of undefined (reading 'idToken'), but it's still able to return the user attributes correctly.

Meags27 commented 11 months ago

@nadetastic The next problem is if I call signOut() or signOut({global: true}), it deletes the cookies/local storage as expected. But if I then call signInWithRedirect() later from a "Login" button, it automatically logs the user in. I can see the code and state gets re-populated in the browser url and it re-creates the cookies again.

I would expect that if a user clicks logout, they would be logged out and when wanting to login again, they would get redirected to the hosted UI and not be auto logged in.

It also doesn't matter if I close the session tab and open another one in the same browser, it would auto log the user in again when clicking "Login".

Duckinm commented 11 months ago

How can I get type support on withAmplify hoc? the error occurred why I try to update my next.config.mjs. Do the ESM support on this wrapper yet?

import { withAmplify } from '@aws-amplify/adapter-nextjs/with-amplify'
import config from './lib/amplify'

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
}

const nextConfigWithAmplify = withAmplify(nextConfig, config)
export default nextConfigWithAmplify
HuiSF commented 11 months ago

Hi @Duckinm currently Amplify doesn't support the Node.js ESM system (mjs) without a transpiler (the next.config file is not being transpiled by Next.js).

Any specific reason that you need to use ESM while Next.js is using next.config.js by default please?

louisthomaspro commented 11 months ago

Is there a way to configure sameSite cookies ? Or development are still pending ?

nadetastic commented 11 months ago

@Meags27 I briefly experienced this issue (user auto sign in) but haven't been able to reproduce it again. After the user signs out, are you able to see any data in local storage and/or cookie storage in the browser?

Meags27 commented 11 months ago

@nadetastic Once signed out, there is nothing in local/cookie/session storage. I checked the network tab and after signing out and clicking login again, it makes a GET request to authorize?redirect_uri etc. and the cookie that is passed in looks like this Cookie: cognito-fl="W10="; csrf-state=""; csrf-state-legacy=""; XSRFTOKEN=[redacted];cognito=[redacted] then it makes a call to cognito token endpoint too and puts the tokens back in.

I wonder if it's to do with the Hosted UI 15 minute session store. In the past, if it was within 15 minutes or so of signing out, it would direct you to the hosted UI and let you click the account to auto login, or you could choose to login normally. But that was after being redirected to the Hosted UI.

Currently testing if it still auto logs in after 15 mins of being logged out.

Edit: After 15 mins, it still auto logged in but after an hour, it sends to the hosted UI to login as expected.

johnf commented 11 months ago

With react native when I do

import { Hub } from 'aws-amplify/utils';

I get the following error

error: Error: Unable to resolve module aws-amplify/utils from /home/johnf/work/gladly/mobile/src/core/auth.ts: aws-amplify/utils could not be found within the project or in these directories:

I'm temp working around with

import { Hub } from '@aws-amplify/core';
nadetastic commented 11 months ago

Hi @louisthomaspro @mattiLeBlanc following up here - cookieStorage is not available with the dev preview but will be included after the General Availability of v6. We will provide information on usage at that time.

nadetastic commented 11 months ago

Hi @johnf, could you confirm what version of aws-amplify you have installed in your package.json?

Duckinm commented 11 months ago

NOTE: I haven't set up any Amplify backend on the project and I don't intend to. Will this cause any issues down the line?

@HuiSF, I configured my setup in a ts file, but when I started using next.config.js, it caused an error while trying to run the dev server.

import type { ResourcesConfig } from 'aws-amplify/core'
^^^^^^

SyntaxError: Cannot use import statement outside a module

In the documentation, it only discusses using config from the default Amplify file. However, I've configured it myself using a self-config pattern. How can I use it with the withAmplify HOC?

const config = require('./src/amplifyconfiguration.json');

My configuration:

import type { ResourcesConfig } from 'aws-amplify/core'

export const amplifyConfiguration = {
  Auth: {
    Cognito: {
      userPoolId: process.env.NEXT_PUBLIC_AWS_COGNITO_POOL_ID!,
      userPoolClientId: process.env.NEXT_PUBLIC_AWS_COGNITO_APP_CLIENT_ID!,
      identityPoolId: process.env.NEXT_PUBLIC_AWS_COGNITO_IDENTITY_POOL!,
      loginWith: {
        oauth: {
          domain: process.env.NEXT_PUBLIC_APP_URL!,
          scopes: [
            'phone',
            'email',
            'openid',
            'aws.cognito.signin.user.admin',
            'profile',
          ],
          redirectSignIn: [process.env.NEXT_PUBLIC_APP_URL!],
          redirectSignOut: [process.env.NEXT_PUBLIC_APP_URL!],
          responseType: 'token',
        },
      },
    },
  },
  Storage: {
    S3: {
      bucket: process.env.NEXT_PUBLIC_S3_BUCKET_NAME!,
      region: process.env.NEXT_PUBLIC_AWS_REGION!,
    },
  },
} satisfies ResourcesConfig
Meags27 commented 10 months ago

With Next.js and v6 Amplify, when I run fetchAuthSession() on a server component to check if the user is authenticated or not, it returns undefined if a user is being redirected back to your site. E.g a logged in user clicks a "checkout" button, gets redirected to a stripe checkout page, buys something, and gets redirected to your own website, then fetchAuthSession() will return undefined straight away even though the user has cookies and is authenticated.

I wanted to use this function in the navigation bar to conditionally render an <AuthNavBar> or <NotAuthNavBar> but it renders <NotAuthNavBar> when redirected. Passing in {forceRefresh: true} also return undefined. It will only work after refreshing the page.

Middleware also says the user is not authenticated and sends them to /login page.

How best do you handle this situation?

 authenticated = await runWithAmplifyServerContext({

            nextServerContext: {cookies},
            operation: async (contextSpec) => {
                try {
                    const session = await fetchAuthSession(contextSpec, {});
                    return session.tokens !== undefined;
                } catch (error) {
                    console.log(error);
                    return false;
                }
            },
        });
matamatanot commented 10 months ago

Next.js v14 will be released soon, will the scope of Amplify JS Library v6 support remain the same?

https://github.com/vercel/next.js/pull/57342

HuiSF commented 10 months ago

Hi @Duckinm

quote > NOTE: I haven't set up any Amplify backend on the project and I don't intend to. Will this cause any issues down the line? > > @HuiSF, I configured my setup in a ts file, but when I started using next.config.js, it caused an error while trying to run the dev server. > > ```ts > import type { ResourcesConfig } from 'aws-amplify/core' > ^^^^^^ > > SyntaxError: Cannot use import statement outside a module > ``` > > In the documentation, it only discusses using config from the default Amplify file. However, I've configured it myself using a self-config pattern. How can I use it with the withAmplify HOC? > > `const config = require('./src/amplifyconfiguration.json');` > > My configuration: > > ```ts > import type { ResourcesConfig } from 'aws-amplify/core' > > export const amplifyConfiguration = { > Auth: { > Cognito: { > userPoolId: process.env.NEXT_PUBLIC_AWS_COGNITO_POOL_ID!, > userPoolClientId: process.env.NEXT_PUBLIC_AWS_COGNITO_APP_CLIENT_ID!, > identityPoolId: process.env.NEXT_PUBLIC_AWS_COGNITO_IDENTITY_POOL!, > loginWith: { > oauth: { > domain: process.env.NEXT_PUBLIC_APP_URL!, > scopes: [ > 'phone', > 'email', > 'openid', > 'aws.cognito.signin.user.admin', > 'profile', > ], > redirectSignIn: [process.env.NEXT_PUBLIC_APP_URL!], > redirectSignOut: [process.env.NEXT_PUBLIC_APP_URL!], > responseType: 'token', > }, > }, > }, > }, > Storage: { > S3: { > bucket: process.env.NEXT_PUBLIC_S3_BUCKET_NAME!, > region: process.env.NEXT_PUBLIC_AWS_REGION!, > }, > }, > } satisfies ResourcesConfig > ```

As the Next.js documentation pointed out:

next.config.js is a regular Node.js module

This means that this file would be executed without a transpiler; thus, importing code written in ts into this file won't work.

However, the withAmplify util function is typed, and you can do your manual configuration inline within the next.config.js file. e.g., in your next.config.js

const { withAmplify } = require('@aws-amplify/adapter-nextjs/with-amplify');

/** @type {import('next').NextConfig} */
const nextConfig = {};

module.exports = withAmplify(nextConfig, {
    Auth: {
        Cognito: {
            userPoolId: 'user_pool_id'
        }
    }
});
image
HuiSF commented 10 months ago

Hi @Meags27 this is due to the adapter currently setting the cookies with sameSite: strict by default. When your end user redirected back to your domain from stripe checkout page, as it's a cross domain transition, browser doesn't send the cookies to your Next.js.

We are planning to support configuring cookie setting options after the General Availability of v6, but I will look into if we can use a loose configuration first.

HuiSF commented 10 months ago

Hi @matamatanot Thank you very much for the heads-up. It depends on how much would change with Next.js 14. We would need to run some tests to ensure compatibility before upgrading the supported version range. This upgrade can come later to v6, incrementally.