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.43k stars 2.13k forks source link

Ampliy-Auth V6 Nextjs SSR "Sign in before calling this API again." #12827

Closed usullu closed 8 months ago

usullu commented 10 months ago

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

Authentication

Amplify Version

v6

Amplify Categories

auth

Backend

Amplify Gen 2 (Preview)

Environment information

``` System: OS: macOS 13.5.1 CPU: (10) arm64 Apple M1 Pro Memory: 88.22 MB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 20.7.0 - ~/.nvm/versions/node/v20.7.0/bin/node Yarn: 1.22.19 - ~/.nvm/versions/node/v20.7.0/bin/yarn npm: 10.1.0 - ~/.nvm/versions/node/v20.7.0/bin/npm pnpm: 8.6.11 - /usr/local/bin/pnpm Browsers: Chrome: 119.0.6045.199 Chrome Canary: 121.0.6165.0 Safari: 16.6 npmPackages: @ampproject/toolbox-optimizer: undefined () @aws-amplify/adapter-nextjs: ^1.0.9 => 1.0.9 @aws-amplify/adapter-nextjs/api: undefined () @aws-amplify/adapter-nextjs/data: undefined () @aws-amplify/backend: ^0.7.0 => 0.7.0 @aws-amplify/backend-cli: ^0.9.2 => 0.9.2 @aws-amplify/ui-react: ^6.0.7 => 6.0.7 @aws-amplify/ui-react-internal: undefined () @babel/core: undefined () @babel/runtime: 7.22.5 @edge-runtime/cookies: 4.0.2 @edge-runtime/ponyfill: 2.4.1 @edge-runtime/primitives: 4.0.2 @hapi/accept: undefined () @mswjs/interceptors: undefined () @napi-rs/triples: undefined () @next/font: undefined () @next/react-dev-overlay: undefined () @opentelemetry/api: undefined () @segment/ajv-human-errors: undefined () @tiptap/extension-placeholder: ^2.1.13 => 2.1.13 @tiptap/extension-underline: ^2.1.13 => 2.1.13 @tiptap/pm: ^2.1.13 => 2.1.13 @tiptap/react: ^2.1.13 => 2.1.13 @tiptap/starter-kit: ^2.1.13 => 2.1.13 @types/node: ^20 => 20.10.5 @types/react: ^18 => 18.2.46 @types/react-dom: ^18 => 18.2.18 @vercel/nft: undefined () @vercel/og: 0.5.15 acorn: undefined () amphtml-validator: undefined () anser: undefined () arg: undefined () assert: undefined () async-retry: undefined () async-sema: undefined () aws-amplify: ^6.0.10 => 6.0.10 aws-amplify/adapter-core: undefined () aws-amplify/analytics: undefined () aws-amplify/analytics/kinesis: undefined () aws-amplify/analytics/kinesis-firehose: undefined () aws-amplify/analytics/personalize: undefined () aws-amplify/analytics/pinpoint: undefined () aws-amplify/api: undefined () aws-amplify/api/server: undefined () aws-amplify/auth: undefined () aws-amplify/auth/cognito: undefined () aws-amplify/auth/cognito/server: undefined () aws-amplify/auth/enable-oauth-listener: undefined () aws-amplify/auth/server: undefined () aws-amplify/datastore: undefined () aws-amplify/in-app-messaging: undefined () aws-amplify/in-app-messaging/pinpoint: undefined () aws-amplify/push-notifications: undefined () aws-amplify/push-notifications/pinpoint: undefined () aws-amplify/storage: undefined () aws-amplify/storage/s3: undefined () aws-amplify/storage/s3/server: undefined () aws-amplify/storage/server: undefined () aws-amplify/utils: undefined () babel-packages: undefined () browserify-zlib: undefined () browserslist: undefined () buffer: undefined () bytes: undefined () ci-info: undefined () cli-select: undefined () client-only: 0.0.1 comment-json: undefined () compression: undefined () conf: undefined () constants-browserify: undefined () content-disposition: undefined () content-type: undefined () cookie: undefined () cross-spawn: undefined () crypto-browserify: undefined () css.escape: undefined () data-uri-to-buffer: undefined () debug: undefined () devalue: undefined () domain-browser: undefined () edge-runtime: undefined () eslint: ^8 => 8.56.0 eslint-config-next: 14.0.4 => 14.0.4 events: undefined () find-cache-dir: undefined () find-up: undefined () fresh: undefined () get-orientation: undefined () glob: undefined () gzip-size: undefined () http-proxy: undefined () http-proxy-agent: undefined () https-browserify: undefined () https-proxy-agent: undefined () icss-utils: undefined () ignore-loader: undefined () image-size: undefined () is-animated: undefined () is-docker: undefined () is-wsl: undefined () jest-worker: undefined () json5: undefined () jsonwebtoken: undefined () loader-runner: undefined () loader-utils: undefined () lodash.curry: undefined () lru-cache: undefined () micromatch: undefined () mini-css-extract-plugin: undefined () nanoid: undefined () native-url: undefined () neo-async: undefined () next: 14.0.4 => 14.0.4 node-fetch: undefined () node-html-parser: undefined () ora: undefined () os-browserify: undefined () p-limit: undefined () path-browserify: undefined () platform: undefined () postcss-flexbugs-fixes: undefined () postcss-modules-extract-imports: undefined () postcss-modules-local-by-default: undefined () postcss-modules-scope: undefined () postcss-modules-values: undefined () postcss-preset-env: undefined () postcss-safe-parser: undefined () postcss-scss: undefined () postcss-value-parser: undefined () process: undefined () punycode: undefined () qr-code-styling: ^1.6.0-rc.1 => 1.6.0-rc.1 querystring-es3: undefined () raw-body: undefined () react: ^18 => 18.2.0 react-builtin: undefined () react-dom: ^18 => 18.2.0 react-dom-builtin: undefined () react-dom-experimental-builtin: undefined () react-experimental-builtin: undefined () react-icons: ^4.12.0 => 4.12.0 react-is: 18.2.0 react-refresh: 0.12.0 react-server-dom-turbopack-builtin: undefined () react-server-dom-turbopack-experimental-builtin: undefined () react-server-dom-webpack-builtin: undefined () react-server-dom-webpack-experimental-builtin: undefined () react-toastify: ^9.1.3 => 9.1.3 regenerator-runtime: 0.13.4 sass-loader: undefined () scheduler-builtin: undefined () scheduler-experimental-builtin: undefined () schema-utils: undefined () semver: undefined () send: undefined () server-only: 0.0.1 setimmediate: undefined () shell-quote: undefined () source-map: undefined () stacktrace-parser: undefined () stream-browserify: undefined () stream-http: undefined () string-hash: undefined () string_decoder: undefined () strip-ansi: undefined () superstruct: undefined () tar: undefined () terser: undefined () text-table: undefined () timers-browserify: undefined () tty-browserify: undefined () typescript: ^5.3.3 => 5.3.3 (4.4.4) ua-parser-js: undefined () unistore: undefined () util: undefined () vm-browserify: undefined () watchpack: undefined () web-vitals: undefined () webpack: undefined () webpack-sources: undefined () ws: undefined () zod: undefined () npmGlobalPackages: @aws-amplify/cli: 12.5.1 corepack: 0.19.0 create-next-app: 14.0.4 npm: 10.1.0 yarn: 1.22.19 ```

Describe the bug

No SSR -> I cannot see on the middleware that the use is logged in

with SSR -> I get an error saying that the user has to be signed in to use the api (to signin)

Expected behavior

Hello,

I have a strange bug on using sign in with the new amplify v6.

The registration is checked by the middleware

middleware.ts

import { fetchAuthSession } from "aws-amplify/auth/server"; import { NextRequest, NextResponse } from "next/server"; import { runWithAmplifyServerContext } from "./utils/aws/server-utils"; export async function middleware(request: NextRequest) { const response = NextResponse.next(); 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; } }, }); console.log(authenticated); if (authenticated) { return response; } const parsedURL = new URL(request.url); const path = parsedURL.pathname; return NextResponse.redirect(new URL(`/auth/login?origin=${path}`, request.url)); } export const config = { matcher: ["/dashboard/:path*","/account/:path*"], };`

If I use the Authenticator from @aws-amplify/ui-react everything works as expected.

Now I try to use a custom server component for the signin flow:

auth/login/page.tsx

` import styles from "@/ui/auth/auth.module.css"; import {redirect} from "next/navigation"; import {handleSignIn} from "@/lib/handleAuth"; import {Amplify} from "aws-amplify"; import config from "../../../../amplifyconfiguration.json"; Amplify.configure(config, {ssr: true}); async function loginButtonPressed(formData: FormData) { "use server" let username = formData.get('email')?.toString() ?? ''; let password = formData.get('password')?.toString() ?? ''; await handleSignIn({username, password}).then(() => { redirect('/dashboard'); } ); } export default function Login() { return(
logo Title

Anmelden

Nicht registriert? Registrieren

); } `

If now I try to login, I get the Error:

error signing in UserUnAuthenticatedException: User needs to be authenticated to call this API. and underlyingError: undefined, recoverySuggestion: 'Sign in before calling this API again.', constructor: [class AuthError extends AmplifyError]

When I change the line Amplify.configure(config, {ssr: true}); to Amplify.configure(config); then it is a success and the signInStep: 'DONE' but the middleware says to authenticated false and redirects me everytime back to the login page.

What am I doing wrong?

The custom signup component is working without a problem.

Reproduction steps

  1. Create a new project like the original docs for amplify gen 2
  2. Create the middleware
  3. Make a login page like the docs says for custom signin

Code Snippet

// Put your code below this line.

Log output

``` error signing in UserUnAuthenticatedException: User needs to be authenticated to call this API. at assertAuthTokens (webpack-internal:///(rsc)/./node_modules/@aws-amplify/auth/dist/esm/providers/cognito/utils/types.mjs:24:15) at getCurrentUser (webpack-internal:///(rsc)/./node_modules/@aws-amplify/auth/dist/esm/providers/cognito/apis/internal/getCurrentUser.mjs:17:71) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async signInWithSRP (webpack-internal:///(rsc)/./node_modules/@aws-amplify/auth/dist/esm/providers/cognito/apis/signInWithSRP.mjs:72:23) at async handleSignIn (webpack-internal:///(rsc)/./src/lib/handleAuth.ts:19:42) at async $$ACTION_0 (webpack-internal:///(rsc)/./src/app/auth/login/page.tsx:33:5) at async /Users/WebstormProjects/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:39:406 at async t0 (/Users/WebstormProjects/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:38:5773) at async rh (/Users/WebstormProjects/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:39:23636) at async doRender (/Users/WebstormProjects/node_modules/next/dist/server/base-server.js:1391:30) at async cacheEntry.responseCache.get.routeKind (/Users/WebstormProjects/node_modules/next/dist/server/base-server.js:1552:28) at async DevServer.renderToResponseWithComponentsImpl (/Users/WebstormProjects/node_modules/next/dist/server/base-server.js:1460:28) at async DevServer.renderPageComponent (/Users/WebstormProjects/node_modules/next/dist/server/base-server.js:1843:24) at async DevServer.renderToResponseImpl (/Users/WebstormProjects/node_modules/next/dist/server/base-server.js:1881:32) at async DevServer.pipeImpl (/Users/WebstormProjects/node_modules/next/dist/server/base-server.js:909:25) at async NextNodeServer.handleCatchallRenderRequest (/Users/WebstormProjects/node_modules/next/dist/server/next-server.js:266:17) at async DevServer.handleRequestImpl (/Users/WebstormProjects/node_modules/next/dist/server/base-server.js:805:17) { underlyingError: undefined, recoverySuggestion: 'Sign in before calling this API again.', constructor: [class AuthError extends AmplifyError] } ```

aws-exports.js


{
  "aws_project_region": "eu-central-1",
  "aws_user_pools_id": "eu-central-1_xxxxxxxx",
  "aws_user_pools_web_client_id": "xxxxxxxxxxxxxxxxxxxx",
  "aws_cognito_region": "eu-central-1",
  "aws_cognito_identity_pool_id": "eu-central-1:xxxxxxxxxxxxxxxxxxxxxx",
  "aws_cognito_signup_attributes": [
    "EMAIL"
  ],
  "aws_cognito_username_attributes": [
    "EMAIL"
  ],
  "aws_cognito_verification_mechanisms": [
    "EMAIL"
  ],
  "aws_cognito_password_protection_settings": {
    "passwordPolicyMinLength": 8,
    "passwordPolicyCharacters": [
      "REQUIRES_NUMBERS",
      "REQUIRES_LOWERCASE",
      "REQUIRES_UPPERCASE",
      "REQUIRES_SYMBOLS"
    ]
  },
  "aws_appsync_graphqlEndpoint": "https://xxxxxxxxxxxxxxxxxxxx.appsync-api.eu-central-1.amazonaws.com/graphql",
  "aws_appsync_region": "eu-central-1",
  "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
  "aws_appsync_additionalAuthenticationTypes": "AWS_IAM",
  "modelIntrospection": {
    "version": 1,
    "models": {
      "Todo": {
        "name": "Todo",
        "fields": {
          "id": {
            "name": "id",
            "isArray": false,
            "type": "ID",
            "isRequired": true,
            "attributes": []
          },
          "name": {
            "name": "name",
            "isArray": false,
            "type": "String",
            "isRequired": false,
            "attributes": []
          },
          "content": {
            "name": "content",
            "isArray": false,
            "type": "String",
            "isRequired": false,
            "attributes": []
          },
          "isDone": {
            "name": "isDone",
            "isArray": false,
            "type": "Boolean",
            "isRequired": false,
            "attributes": []
          },
          "owner": {
            "name": "owner",
            "isArray": false,
            "type": "String",
            "isRequired": false,
            "attributes": []
          },
          "createdAt": {
            "name": "createdAt",
            "isArray": false,
            "type": "AWSDateTime",
            "isRequired": true,
            "attributes": []
          },
          "updatedAt": {
            "name": "updatedAt",
            "isArray": false,
            "type": "AWSDateTime",
            "isRequired": true,
            "attributes": []
          }
        },
        "syncable": true,
        "pluralName": "Todos",
        "attributes": [
          {
            "type": "model",
            "properties": {}
          },
          {
            "type": "key",
            "properties": {
              "fields": [
                "id"
              ]
            }
          },
          {
            "type": "auth",
            "properties": {
              "rules": [
                {
                  "provider": "userPools",
                  "ownerField": "owner",
                  "allow": "owner",
                  "identityClaim": "cognito:username",
                  "operations": [
                    "create",
                    "update",
                    "delete",
                    "read"
                  ]
                },
                {
                  "allow": "private",
                  "operations": [
                    "create",
                    "update",
                    "delete",
                    "read"
                  ]
                }
              ]
            }
          }
        ],
        "primaryKeyInfo": {
          "isCustomPrimaryKey": false,
          "primaryKeyFieldName": "id",
          "sortKeyFieldNames": []
        }
      },
    "enums": {},
    "nonModels": {}
  }
}

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

amplify/auth/resource.ts

`import { defineAuth } from '@aws-amplify/backend'; /** * Define and configure your auth resource * When used alongside data, it is automatically configured as an auth provider for data * @see https://docs.amplify.aws/gen2/build-a-backend/auth */ export const auth = defineAuth({ loginWith: { email: true, // add social providers externalProviders: { /** * first, create your secrets using `amplify sandbox secret` * then, import `secret` from `@aws-amplify/backend` * @see https://docs.amplify.aws/gen2/deploy-and-host/sandbox-environments/features/#setting-secrets */ // loginWithAmazon: { // clientId: secret('LOGINWITHAMAZON_CLIENT_ID'), // clientSecret: secret('LOGINWITHAMAZON_CLIENT_SECRET'), // } }, }, /** * enable multifactor authentication * @see https://docs.amplify.aws/gen2/build-a-backend/auth/manage-mfa */ // multifactor: { // mode: 'OPTIONAL', // sms: { // smsMessage: (code) => `Your verification code is ${code}`, // }, // }, userAttributes: { /** request additional attributes for your app's users */ // profilePicture: { // mutable: true, // required: false, // }, }, }); `

amplify/backend.ts

` import { defineBackend } from '@aws-amplify/backend'; import { auth } from './auth/resource'; import { data } from './data/resource'; const backend = defineBackend({ auth, data, }); `
cwomack commented 10 months ago

Hey, @usullu 👋 and thanks for opening this issue. After looking at the error you referenced, it seems you're trying to call the signIn API in a server action. That isn't supported yet, but we do have feature requests in the repo for this such as #11598.

Additionally, you can refer to the Supported API's for Next.js server-side usage section of the docs to check which ones would work for server side actions. Hopefully that helps clarify why these errors are being thrown, and definitely give that linked feature request a comment or upvote to help it gain traction!

usullu commented 10 months ago

Hey @cwomack, you are my hero! I am trying to solve this problem since two days and you solve it in minutes. Thank you very much!

After using client components without server actions it works like native.

Nevertheless the possibility to signin on the server would be a game changer.

nadetastic commented 8 months ago

@usullu glad the comment from @cwomack was able to get you resolved! I'll go ahead and close out this issue as we are tracking the feature request of authenticating on the server side on this issue - https://github.com/aws-amplify/amplify-js/issues/11598

Thanks!