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

Migrating from v5 to v6 Auth, SSR, SvelteKit #12578

Open
3 tasks done
asmajlovicmars opened this issue Nov 16, 2023 · 13 comments
Open
3 tasks done

Migrating from v5 to v6 Auth, SSR, SvelteKit #12578

asmajlovicmars opened this issue Nov 16, 2023 · 13 comments
Labels
feature-request Request a new feature SSR Issues related to Server Side Rendering VP Version parity issues between v5 and v6

Comments

@asmajlovicmars
Copy link

Before opening, please confirm:

JavaScript Framework

Web Components

Amplify APIs

Authentication, GraphQL API

Amplify Categories

auth

Environment information

# Put output below this line
  System:
    OS: macOS 13.6
    CPU: (10) arm64 Apple M1 Pro
    Memory: 65.52 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.9.0 - ~/.volta/tools/image/node/20.9.0/bin/node
    Yarn: 1.22.19 - ~/.volta/bin/yarn
    npm: 10.1.0 - ~/.volta/tools/image/node/20.9.0/bin/npm
    pnpm: 8.10.3 - ~/.volta/bin/pnpm
    bun: 1.0.7 - /opt/homebrew/bin/bun
  Browsers:
    Brave Browser: 119.1.60.114
    Chrome: 119.0.6045.159
    Safari: 17.1
  npmPackages:
    @playwright/test: ^1.39.0 => 1.39.0 
    @sveltejs/adapter-auto: ^2.1.1 => 2.1.1 
    @sveltejs/kit: ^1.27.6 => 1.27.6 
    aws-amplify: ^6.0.2 => 6.0.2 
    prettier: ^3.1.0 => 3.1.0 
    prettier-plugin-svelte: ^3.1.0 => 3.1.0 
    svelte: 5.0.0-next.4 => 5.0.0-next.4 
    svelte-check: ^3.6.0 => 3.6.0 
    tslib: ^2.6.2 => 2.6.2 
    typescript: ^5.2.2 => 5.2.2 
    vite: ^4.5.0 => 4.5.0 
  npmGlobalPackages:
    corepack: 0.20.0
    npm: 10.1.0

Describe the bug

I'm excited about the v6, and started testing a migration, but quickly got stuck on SSR side.

// v5 - server side - this used to work seamlessly

import { Amplify, withSSRContext } from 'aws-amplify';
import { awsmobile } from '$lib/utils/aws/private-envs';

export const handle = async ({ event, resolve }): Promise<Response> => {
  const { request } = event;

  const req = {
    ...request,
    headers: {
      ...request.headers,
      cookie: request.headers.get('cookie'),
    },
  };

  Amplify.configure({ ...awsmobile, ssr: true });
  const { Auth } = withSSRContext({ req });

  // ...
};

Now I'm trying to get it going with v6 on the server side, and not sure how to do it:

import type { PageServerLoad } from './$types';
import { Amplify } from 'aws-amplify';
import { fetchAuthSession, getCurrentUser } from 'aws-amplify/auth/server';
import awsconfig from '../../aws-exports';

export const load = (async ({ request, cookies }) => {
	const c = cookies.getAll();
	console.log(c);

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

		const session = await fetchAuthSession({
			token: {
				value: Symbol('eyJraWQiOi...')
			}
		});

		const user = await getCurrentUser({
			token: {
				value: Symbol('eyJraWQiOiI...')
			}
		});

		// regardless if it's a valid idToken, accessToken, or refreshToken, the following error is thrown:
		// e: Error: Attempted to get the Amplify Server Context that may have been destroyed.

		console.log({ session, user });
	} catch (e) {
		console.log(e);
	}
	return {};
}) satisfies PageServerLoad;

However, on the V6 client side, everything works as expected:

<script lang="ts">
	import { onMount } from 'svelte';
	import type { PageData } from './$types';

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

	export let data: PageData;

	onMount(async () => {
		Amplify.configure(awsconfig);
		const user = await getCurrentUser();
		console.log({ user });

		if (!user) {
			const signedIn = await signIn({
				username: '...',
				password: '...'
			});
			console.log({ signedIn });
		}
	});
</script>

Expected behavior

Expected behaviour is that the server side authenticates, and provides a cognitoUser same as in V5.

Reproduction steps

  1. SvelteKit:
  2. npm create svelte@latest my-app
  3. cd my-app
  4. npm install
  5. npm run dev
  6. amplify init
  7. amplify import auth
  8. amplify push

Code Snippet

I'm excited about the v6, and started testing a migration, but quickly got stuck on SSR side.

// v5 - server side - this used to work seamlessly

import { Amplify, withSSRContext } from 'aws-amplify';
import { awsmobile } from '$lib/utils/aws/private-envs';

export const handle = async ({ event, resolve }): Promise<Response> => {
  const { request } = event;

  const req = {
    ...request,
    headers: {
      ...request.headers,
      cookie: request.headers.get('cookie'),
    },
  };

  Amplify.configure({ ...awsmobile, ssr: true });
  const { Auth } = withSSRContext({ req });

  // ...
};

Now I'm trying to get it going with v6 on the server side, and not sure how to do it:

import type { PageServerLoad } from './$types';
import { Amplify } from 'aws-amplify';
import { fetchAuthSession, getCurrentUser } from 'aws-amplify/auth/server';
import awsconfig from '../../aws-exports';

export const load = (async ({ request, cookies }) => {
	const c = cookies.getAll();
	console.log(c);

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

		const session = await fetchAuthSession({
			token: {
				value: Symbol('eyJraWQiOi...idToken or accessToken from cognito cookies')
			}
		});

		const user = await getCurrentUser({
			token: {
				value: Symbol('eyJraWQiOiI...idToken or accessToken from cognito cookies')
			}
		});

		// regardless if it's a valid idToken, accessToken, or refreshToken, the following error is thrown:
		// e: Error: Attempted to get the Amplify Server Context that may have been destroyed.

		console.log({ session, user });
	} catch (e) {
		console.log(e);
	}
	return {};
}) satisfies PageServerLoad;

However, on the V6 client side, everything works as expected:

<script lang="ts">
	import { onMount } from 'svelte';
	import type { PageData } from './$types';

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

	export let data: PageData;

	onMount(async () => {
		Amplify.configure(awsconfig);
		const user = await getCurrentUser();
		console.log({ user });

		if (!user) {
			const signedIn = await signIn({
				username: '...',
				password: '...'
			});
			console.log({ signedIn });
		}
	});
</script>

Log output

// Put your logs below this line

Error: Attempted to get the Amplify Server Context that may have been destroyed.
    at getAmplifyServerContext (file:///Users/asmajlovic/code/dexp-fe-v2/node_modules/.pnpm/@aws-amplify+core@6.0.2/node_modules/@aws-amplify/core/dist/esm/adapterCore/serverContext/serverContext.mjs:29:11)
    at Proxy.fetchAuthSession (file:///Users/asmajlovic/code/dexp-fe-v2/node_modules/.pnpm/@aws-amplify+core@6.0.2/node_modules/@aws-amplify/core/dist/esm/singleton/apis/server/fetchAuthSession.mjs:9:31)
    at load (/Users/asmajlovic/code/dexp-fe-v2/src/routes/be/+page.server.ts:16:49)
    at Module.load_server_data (/Users/asmajlovic/code/dexp-fe-v2/node_modules/.pnpm/@sveltejs+kit@1.27.6_svelte@5.0.0-next.4_vite@4.5.0/node_modules/@sveltejs/kit/src/runtime/server/page/load_data.js:57:41)
    at eval (/Users/asmajlovic/code/dexp-fe-v2/node_modules/.pnpm/@sveltejs+kit@1.27.6_svelte@5.0.0-next.4_vite@4.5.0/node_modules/@sveltejs/kit/src/runtime/server/page/index.js:150:41)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)


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

@asmajlovicmars asmajlovicmars added the pending-triage Issue is pending triage label Nov 16, 2023
@nadetastic nadetastic added the SSR Issues related to Server Side Rendering label Nov 16, 2023
@nadetastic nadetastic self-assigned this Nov 16, 2023
@nadetastic
Copy link
Member

Hi @asmajlovicmars, thank you for opening this issue.

With Amplify JS v6 we provide an adapter for only Next.js, although the underlying generic adapters may be used with other frameworks. We will provide instructions of using the generic adapters with other frameworks soon, but for now II'll mark this as a feature request and follow up once the instructions are available.

@nadetastic nadetastic added feature-request Request a new feature and removed pending-triage Issue is pending triage labels Nov 16, 2023
@taoatmars
Copy link

@nadetastic same problem here looks like we have a situation that will break our production code and no way around it. is there a possibility that you can point me in the right direction for how to use the generic adapters?

@asmajlovicmars
Copy link
Author

Primarily, these modifications enable compatibility between SvelteKit and Amplify v6, supporting both client-side rendering (CSR) and server-side rendering (SSR). While the entire application has not been converted yet, and all edge cases have not been tested, this marks a progressive step towards full integration.

// signin.svelte
// ...
import amplifyConfig from '../../amplifyconfiguration.json';
Amplify.configure({ Auth: authConfig }, { ssr: true }); // ssr: true will enable creation of cookies
await signIn({username: '...', password: '...'}); // will create CognitoIdentity jwt cookies
invalidateAll() // or use a milder version, just to make a new request, and trigger hooks
// ...
// hooks.server.ts
import type { Handle } from '@sveltejs/kit';
import amplifyConfig from '../../amplifyconfiguration.json';

import { Amplify } from 'aws-amplify';
// import { getCookiePayload, getSpecificCookie } from '$lib/utils/cookie-helpers';

type Cookie = { name: string; value: string } | null;

const getSpecificCookie = (cookies: any, name: string) =>
	cookies
		.map((cookie: Cookie) => (cookie?.name?.endsWith(name) ? cookie?.value : null))
		.filter((value: string) => value !== null)[0];

const getCookiePayload = (cookie: string) => {
	const payload = cookie?.split('.')[1];
	const parsedPayload = JSON.parse(atob(payload));
	return parsedPayload;
};

Amplify.configure(amplifyConfig, { ssr: true }); // not sure if { ssr: true } is a must, or needed here...

export const handleUser = (async ({ event, resolve }): Promise<Response> => {
	const { request, cookies, locals, getClientAddress } = event;

	const allCookies = cookies.getAll();
	const cognitoCookies = allCookies.filter(
		(cookie) => cookie.name.startsWith('CognitoIdentityServiceProvider') && cookie.value !== null
	);

	if (cognitoCookies?.length === 0) {
		locals.jwtAccessToken = null;
		locals.jwtIdToken = null;
		locals.idTokenPayload = null;
		locals.accessTokenPayload = null;

		locals.userId = null;
		locals.username = null;
		locals.email = null;
		locals.userGroups = null;

		console.log('❌ no cookies:');

		return resolve(event);
	}

	const idToken = getSpecificCookie(cognitoCookies, 'idToken');
	const accessToken = getSpecificCookie(cognitoCookies, 'accessToken');
	const idTokenPayload = idToken && getCookiePayload(idToken);
	const accessTokenPayload = accessToken && getCookiePayload(accessToken);

	locals.jwtAccessToken = accessToken;
	locals.jwtIdToken = idToken;
	locals.idTokenPayload = idTokenPayload;
	locals.accessTokenPayload = accessTokenPayload;

	locals.userId = idTokenPayload?.sub;
	locals.username = idTokenPayload?.sub;
	locals.email = idTokenPayload?.email;
	locals.userGroups = idTokenPayload?.['cognito:groups'];

	console.log('✅ signed-in email:', locals.email);
	return resolve(event);
}) satisfies Handle;
// +layout.svelte or any other +page.svelte
<script lang="ts">
	import { fetchAuthSession, getCurrentUser } from '@aws-amplify/auth';
	import { Amplify, type ResourcesConfig } from 'aws-amplify';
	import amplifyConfig from '../amplifyconfiguration.json';


	import type { LayoutData } from './$types';
	import { onMount } from 'svelte';

	export let data: LayoutData;

	const authConfig: ResourcesConfig['Auth'] = {
		Cognito: {
			userPoolId: amplifyConfig.aws_user_pools_id,
			userPoolClientId: amplifyConfig.aws_user_pools_web_client_id
			}
	};

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

	onMount(async () => {
		await fetchAuthSession(); // will refresh tokens!!! 🎉
	});
</script>

<nav>
	<ul class="horizontal-nav">
		<li>
			<a href="/">Home</a>
		</li>
		<li>
			<a href="/signin">Sign In</a>
		</li>
	</ul>
</nav>
<slot />

@swaminator
Copy link

@asmajlovicmars thank you for your request. To improve the SSR experience in V6, we have moved to an adapter pattern that requires a custom implementation per framework. While we have shipped V6 with a Next.js adapter built-in, we would love to support more. In the next few days we are going to publish a document about the adapter with an example of how to build one. We are treating this issue as priority and will get back to you once we have the document published.

@asmajlovicmars
Copy link
Author

Thank you, @swaminator! I'm progressing through the migration from version 5 to 6, and things are improving. However, it would be much easier and more efficient to use a dedicated Vite/SvelteKit adapter. I'm looking forward to it, especially since we've committed to using both Amplify and SvelteKit, and not giving up 😁

@nadetastic nadetastic mentioned this issue Dec 8, 2023
2 tasks
@nadetastic
Copy link
Member

Hi @asmajlovicmars @taoatmars following up here - we have published documentation on how to use the generic adapters and can be viewed here - https://docs.amplify.aws/react/build-a-backend/server-side-rendering/

Let me know if you have any questions.

@asmajlovicmars
Copy link
Author

This is excellent! Thank you @nadetastic. Will test ASAP.

@asmajlovicmars
Copy link
Author

@nadetastic, @swaminator Thank you both!

Just two corrections in the docs, getCurrentUser import on line 8, and line 57 should be called with the context:

line 8: import { fetchAuthSession, getCurrentUser } from 'aws-amplify/auth/server';

line 57: const { username } = await getCurrentUser(contextSpec);

@kewur
Copy link

kewur commented Jan 21, 2024

hi, I'm trying to have calls to my backend in asp.net and from my angular application. I understand the documentation and the sample on here, however, I'm unsure how to make the calls with the amplify auth token on the first load of the page, I understand that behind the scenes a cookie is being set and the server uses the provider to get said cookie (that has auth tokens).

my first question: are all of the calls that needs to return user specific context all needs to be wrapped with the "runWithAmplifyServerContext" from the client side? Or did I misunderstood something here, does "runWithAmplifyServerContext" replace Amplify.configure() ?

second question: my ssr provider is angular ssr, however my backends that needs to provide user context are in ASP.net core, do I need to create a custom solution that extracts cookies from http requests if I was to onboard with what's happening here?

would love it if I can get some clarity on if this solution helps my case or I need to implement something on my own for this

@nadetastic nadetastic added the V6 V6 of Amplify JS label Jan 23, 2024
@benmccann
Copy link

I don't know if it addresses with this issue specifically, but there is now a SvelteKit Amplify adapter: https://github.com/gzimbron/amplify-adapter. If there are any issues, it'd be great to solve them there so that folks don't have to re-invent the wheel. The full list of adapters is available on https://www.sveltesociety.dev/packages?category=sveltekit-adapters - a few platforms have multiple adapters available

If anyone from AWS is interested in adding official first-party support for SvelteKit, please ping me and I'd be happy to add you to SvelteKit's partners program so that you can get direct support from the maintainers in supporting your integration

@dlamon1
Copy link

dlamon1 commented Jul 8, 2024

@benmccann The amplify-adapter does not include methods for handling server side cookies or generating the server side API method.

@dlamon1
Copy link

dlamon1 commented Jul 8, 2024

Hi @asmajlovicmars @taoatmars following up here - we have published documentation on how to use the generic adapters and can be viewed here - https://docs.amplify.aws/react/build-a-backend/server-side-rendering/

Let me know if you have any questions.

Have the docs changed ? I don't see a guide for generic implementation

@HuiSF
Copy link
Member

HuiSF commented Jul 9, 2024

Hi @dlamon1 apologies for the inconvenience, the documentation about the generic SSR adapter currently is available under the "Gen1" selector: https://docs.amplify.aws/gen1/react/build-a-backend/server-side-rendering/

@cwomack cwomack added VP Version parity issues between v5 and v6 and removed V6 V6 of Amplify JS labels Oct 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request Request a new feature SSR Issues related to Server Side Rendering VP Version parity issues between v5 and v6
Projects
None yet
Development

No branches or pull requests

9 participants