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

External Provider Login (Google) does not store tokens in cookieStorage #13963

Open why-silvio opened 3 weeks ago

why-silvio commented 3 weeks ago

Before opening, please confirm:

JavaScript Framework

Not applicable

Amplify APIs

Authentication

Amplify Version

v6

Amplify Categories

auth

Backend

Amplify Gen 2

Environment information

``` # Put output below this line System: OS: macOS 15.0 CPU: (8) arm64 Apple M1 Memory: 59.77 MB / 16.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 18.20.4 - ~/.nvm/versions/node/v18.20.4/bin/node npm: 10.7.0 - ~/.nvm/versions/node/v18.20.4/bin/npm Browsers: Brave Browser: 129.1.70.126 Safari: 18.0 npmPackages: %name%: 0.1.0 @aws-amplify/backend: ^1.0.2 => 1.5.1 @aws-amplify/backend-cli: ^1.0.3 => 1.3.0 @aws-lambda-powertools/logger: ^2.1.1 => 2.10.0 @aws-sdk/client-apigatewaymanagementapi: ^3.577.0 => 3.679.0 @aws-sdk/client-cognito-identity-provider: ^3.577.0 => 3.679.0 @aws-sdk/client-dynamodb: ^3.577.0 => 3.679.0 @aws-sdk/client-lambda: ^3.577.0 => 3.680.0 (3.624.0) @aws-sdk/client-scheduler: ^3.637.0 => 3.679.0 @aws-sdk/client-sfn: ^3.577.0 => 3.679.0 @aws-sdk/lib-dynamodb: ^3.577.0 => 3.679.0 @aws-sdk/util-utf8: ^3.374.0 => 3.374.0 @dotenvx/dotenvx: ^1.11.5 => 1.21.0 @fullcalendar/core: ^6.1.15 => 6.1.15 @fullcalendar/daygrid: ^6.1.15 => 6.1.15 @fullcalendar/interaction: ^6.1.15 => 6.1.15 @fullcalendar/list: ^6.1.15 => 6.1.15 @fullcalendar/multimonth: ^6.1.15 => 6.1.15 @google/generative-ai: ^0.11.3 => 0.11.5 @google/generative-ai-files: undefined () @iconify-json/fa6-brands: ^1.1.19 => 1.2.1 @iconify-json/iconoir: ^1.1.44 => 1.2.2 @iconify-json/lucide: ^1.1.208 => 1.2.10 @playwright/test: ^1.28.1 => 1.48.2 @sveltejs/adapter-auto: ^3.0.0 => 3.3.1 @sveltejs/kit: ^2.5.10 => 2.7.3 @sveltejs/vite-plugin-svelte: ^3.0.0 => 3.1.2 @tailwindcss/typography: ^0.5.13 => 0.5.15 @types/aws-lambda: ^8.10.138 => 8.10.145 @types/eslint: ^8.56.7 => 8.56.12 @types/jsdom: ^21.1.6 => 21.1.7 @types/pg: ^8.11.6 => 8.11.10 amplify-adapter: ^0.1.2 => 0.1.2 autoprefixer: ^10.4.19 => 10.4.20 aws-amplify: ^6.3.2 => 6.6.7 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/data: undefined () aws-amplify/data/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 () aws-cdk: ^2.142.1 => 2.164.1 aws-cdk-lib: ^2.142.1 => 2.164.1 aws-jwt-verify: ^4.0.1 => 4.0.1 axios: ^1.7.1 => 1.7.7 constructs: ^10.3.0 => 10.4.2 daisyui: ^4.11.1 => 4.12.13 date-fns: ^3.6.0 => 3.6.0 esbuild: ^0.21.3 => 0.21.5 (0.23.1) eslint: ^9.0.0 => 9.13.0 eslint-config-prettier: ^9.1.0 => 9.1.0 eslint-plugin-svelte: ^2.36.0 => 2.46.0 fastest-levenshtein: ^1.0.16 => 1.0.16 globals: ^15.0.0 => 15.11.0 (11.12.0, 14.0.0) happy-dom: ^15.0.0 => 15.7.4 node-fetch: ^3.3.2 => 3.3.2 (2.7.0) pg: ^8.11.5 => 8.13.1 (8.11.6) postcss: ^8.4.38 => 8.4.47 prettier: ^3.1.1 => 3.3.3 (2.3.2, 2.8.8, 1.19.1) prettier-plugin-svelte: ^3.1.2 => 3.2.7 prettier-plugin-tailwindcss: ^0.5.1 => 0.5.14 svelte: ^4.2.7 => 4.2.19 svelte-check: ^3.8.0 => 3.8.6 svelte-preprocess: ^5.1.4 => 5.1.4 svelte-radix: ^1.1.0 => 1.1.1 sveltekit-i18n: ^2.4.2 => 2.4.2 tailwind-merge: ^2.3.0 => 2.5.4 tailwind-variants: ^0.2.1 => 0.2.1 tailwindcss: ^3.4.3 => 3.4.14 tslib: ^2.4.1 => 2.8.0 (2.0.3, 2.7.0, 2.4.1, 2.6.3, 2.2.0, 2.3.1, 1.14.1) tsx: ^4.10.5 => 4.19.1 typescript: ^5.4.5 => 5.6.3 (4.4.4, 4.9.5) typescript-eslint: ^8.0.0-alpha.20 => 8.11.0 unplugin-icons: ^0.19.0 => 0.19.3 uuid: ^9.0.1 => 9.0.1 vite: ^5.0.3 => 5.4.10 vitest: ^1.2.0 => 1.6.0 npmGlobalPackages: corepack: 0.28.0 npm: 10.7.0 ```

Describe the bug

I am using sveltekit in combination with amplify Gen2 and I store the authentication tokens in cookieStorage. This works well for email/password authentication but when I want to use the Google Sign In option it writes the tokens in localStorage. It seems it ignores the cookieStorage setup.

I have an authentication page localhost:5173/authentication/login with the button that triggers the Google Sign In. The redirect should happen to localhost:5173/

Expected behavior

Storing the token information in the cookieStorage.

Reproduction steps

export const auth = defineAuth({
  loginWith: {
    email: true,
    externalProviders: {
      google: {
        clientId: secret('GOOGLE_CLIENT_ID'),
        clientSecret: secret('GOOGLE_CLIENT_SECRET'),
        scopes: [
          'profile',
          'email'
        ]
      },
      callbackUrls: [
        'http://localhost:5173/'
      ],
      logoutUrls: ['http://localhost:5173/'],
    }
  },

Code Snippet

// Put your code below this line.

// content of $lib/aws.js which is loaded in my +layout.server.js (server) and the +layout.svelte (client)

import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';
import { CookieStorage } from 'aws-amplify/utils';
import { cognitoUserPoolsTokenProvider } from 'aws-amplify/auth/cognito';
import { fetchAuthSession } from 'aws-amplify/auth';

Amplify.configure({
  ...outputs,
});
const existingConfig = Amplify.getConfig();
Amplify.configure(
  {
    ...existingConfig,
    API: {
      ...existingConfig.API,
      REST: outputs.custom.API
    }
  },
  {
    API: {
      REST: {
        headers: async () => {
          return {
            Authorization: `${(await fetchAuthSession())?.tokens?.accessToken?.toString()}`
          };
        }
      }
    }
  }
);

cognitoUserPoolsTokenProvider.setKeyValueStorage(
  new CookieStorage({
    secure: false //process.env.NODE_ENV !== 'development'
  })
);

Log output

``` // Put your logs below this line ```

aws-exports.js

No response

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

cwomack commented 3 weeks ago

Hello, @why-silvio 👋. Are you by chance experiencing this on Safari specifically? I ask because Webkit-based browsers like Safari will require the secure attribute to be true. The auth tokens won't be written into your cookie store if you're doing the following on Safari:

cognitoUserPoolsTokenProvider.setKeyValueStorage(
  new CookieStorage({
    secure: false //process.env.NODE_ENV !== 'development'
  })
);

HTTPS is recommended anyways as a best practice, but if this is happening on a non-Webkit-based browser then let us know. If this is not happening on Safari though, can you share what you see in the network tabs for the sign-in requests? Are there any exceptions or 4XX codes you can share via a screenshot or text of what the response is?

why-silvio commented 3 weeks ago

Hi @cwomack thank you for your quick reply. I think I found the issue that results in a redirect loop on my end.

I can see that the response from Google SSO gets first stored into the localstorage for some milliseconds and only after maybe 0.5 seconds it gets deleted from localstorage and saved into cookie storage. However, within that time my code already does a redirect as it couldn't find the tokens in the cookiestorage. Is this storage behavior wanted?

I didn't yet figure out how to solve this (so far I only see the cookies properly placed when I stop the redirect process but then the user needs to click a second time on login to actually get redirected to "/" which is not ideal) so I am still looking for a solution that works. Can it be an issue that I handle the redirect and cookie reading on the server side? Like said for the email/password flow it works as fine.

cwomack commented 2 weeks ago

@why-silvio, appreciate the follow up here and additional context. We're going to work on reproducing this on our side and digging into this further, but will follow up soon.