remix-run / remix

Build Better Websites. Create modern, resilient user experiences with web fundamentals.
https://remix.run
MIT License
29.99k stars 2.53k forks source link

Support for aws-amplify? #806

Closed misantronic closed 2 years ago

misantronic commented 2 years ago

Which Remix packages are impacted?

What version of Remix are you using?

1.0.6

Steps to Reproduce

Install aws-amplify: npm i aws-amplify

Use it in the project:

import Amplify from 'aws-amplify';

export default function App() {
    useEffect(() => {
       Amplify.configure({...});
    }, []);

    return (
        <Document>
            <Layout>
                <Outlet />
            </Layout>
        </Document>
    );
}

Expected Behavior

It should work, just like before.

Actual Behavior

I am pretty new to remix and ssr altogether. Also aws states that aws-amplify supports ssr, I cannot get it to work. Whenever I am executing the code above, I get an error:

Uncaught TypeError: Failed to resolve module specifier "url". Relative references must start with either "/", "./", or "../".

I am also curious: Is that code I put into the useEffect actually executed on the server or in the browser? I just would like to run aws-amplify in the browser, to get my old app-logic going...

LukasGerm commented 2 years ago

I have the same error but with the "path" package at the moment.

LukasGerm commented 2 years ago

Okay, hear me out. :)

I think you can't use that module inside the routes path. I just moved my helper function out of the routes folder and now the app is running for me.

misantronic commented 2 years ago

Okay, hear me out. :)

I think you can't use that module inside the routes path. I just moved my helper function out of the routes folder and now the app is running for me.

I have it like this:

/app/root.tsx

import { configureAuth } from './aws/auth';

export default function App() {
    useEffect(() => {
        configureAuth();
    }, []);
  ...
}

/app/aws/auth.ts

import Amplify from 'aws-amplify';

export function configureAuth() {
    console.log(Amplify); // causes error
}

so that structure shouldn't cause any troubles... still it does :(

chanan commented 2 years ago

I have a similar problem trying to use the Authenticator control from @aws-amplify/ui-react.

Just by adding the import to index.jsx:

import { Authenticator } from "@aws-amplify/ui-react";

I get the following error:

Uncaught TypeError: Failed to resolve module specifier "url". Relative references must start with either "/", "./", or "../".

TypeError: Cannot read properties of undefined (reading 'root')
    at RemixRoute (http://localhost:3000/build/_shared/chunk-O3PPVUTT.js:3035:19)
    at renderWithHooks (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:11065:26)
    at mountIndeterminateComponent (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:13185:21)
    at beginWork (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:13972:22)
    at HTMLUnknownElement.callCallback2 (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:3675:22)
    at Object.invokeGuardedCallbackDev (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:3700:24)
    at invokeGuardedCallback (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:3734:39)
    at beginWork$1 (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:17081:15)
    at performUnitOfWork (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:16309:20)
    at workLoopSync (http://localhost:3000/build/_shared/chunk-MHFS6D7O.js:16263:13)

Doing a quick debug, when the import is present then routeModules is undefined on line 179 of components.js.

ryanflorence commented 2 years ago

Somebody who knows AWS Amplify will need to help out here. Remix is pretty simple to host. To deploy a Remix app the server needs to:

  1. Serve static assets out of public/
  2. Route all other requests to a lambda

That lambda would have a Remix adapter that adapts the AWS Amplify request into a web fetch Request. I'm sure the adapter will be pretty much identical to the remix-architect and remix-netlify adapters. So you can look in there for hints.

When I've looked at AWS amplify it all seemed very browser-oriented and it wasn't obvious how to set up those two requirements. We welcome any amplify experts to help out here :)

chanan commented 2 years ago

@ryanflorence do you think the error:

Uncaught TypeError: Failed to resolve module specifier "url". Relative references must start with either "/", "./", or "../".

Is thrown by Remix?

dangreaves commented 2 years ago

Hey guys, I’m on mobile so can’t type out a proper writeup, but the reason for this is because the frontend amplify packages reference node modules like url, path etc.

These modules don’t actually exist in the browser, but are polyfilled when bundling with webpack 4. Note that throughout the amplify documentation, they always reference webpack 4 specifically.

In webpack 5, this polyfill functionality was removed, so it’s my understanding that the amplify packages will not work with webpack 5 without some config.

https://github.com/webpack/changelog-v5

Coming back to remix, I believe remix uses esbuild under the hood which won’t be polyfilling these node libraries, hence the errors.

There’s a giant thread over in the amplify repo with discussion on this.

aws-amplify/amplify-js#9639

Just thought I would provide some context, this is an amplify ui + esbuild issue, nothing to do with remix itself.

I personally think given the backend focus of remix, you probably don’t want to be using the amplify ui packages anyway. Do all your auth, queries etc using the AWS admin SDKs in your loaders and actions. Amplify UI is designed for single page apps, so you will be fighting the remix paradigm when it comes to auth etc.

misantronic commented 2 years ago

Do all your auth, queries etc using the AWS admin SDKs in your loaders and actions.

thank your for this detailed explanation. do you happen to know where I would find the docs for those aws admin sdks?

chanan commented 2 years ago

@misantronic I think he means the aws-sdk which you can find the docs and detailed API here: https://aws.amazon.com/sdk-for-javascript/

@dangreaves Thanks for the explanation. Yes, it should mostly all work via the sdk in the Loader/Action function. One thing that would still be problematic would be Auth when using social/oauth providers as that needs to be done via the browser. I haven't found a way to do that without the library, although it should all be http calls/redirects so presumably there is a way.

ryanflorence commented 2 years ago

Often you do the initial auth dance in the browser, and then submit the JWT to your remix app and put it in a cookie and then use the JWT on the Remix server.

chanan commented 2 years ago

@ryanflorence Right that is the problem, if we do the initial browser part with the library, the app crashes with the error above. Maybe we can do the redirect without the library, not sure yet, I will give it a shot at some point this week.

dangreaves commented 2 years ago

If you were able to add esbuild plugins to remix, you could add a plugin like this which adds a webpack 4-like polyfill feature.

https://github.com/remorses/esbuild-plugins#esbuild-pluginsnode-modules-polyfill

I can’t find a documented way of adding esbuild plugins to remix though so that may not be possible yet.

Also, it’s clear that the amplify team have only tested the package with webpack 4, so you might run into more esbuild related problems. as there’s a lot less “magic” in esbuild (which is a good thing!).

If it’s possible to do your initial auth with an alternate library, I would highly recommend it as the amplify ui package isn’t great for performance, has a lot of side effects and has some design problems (eg the use of node modules, requiring polyfilling at the bundler).

You might have some luck using this package which is specifically for cognito, rather than the full amplify ui package.

https://www.npmjs.com/package/amazon-cognito-identity-js

chanan commented 2 years ago

In case anyone needs it, here is an authentication method for Cognito without Amplify libraries:

https://gist.github.com/chanan/d1601c63df36ac476e84fbc2ca96b2e9

misantronic commented 2 years ago

In case anyone needs it, here is an authentication method for Cognito without Amplify libraries:

https://gist.github.com/chanan/d1601c63df36ac476e84fbc2ca96b2e9

that is incredible helpful — thank you 🙏

jesuscovam commented 2 years ago

something that pops in my mind is that Amplify AppSync (GraphQL API) requires client-side Auth to run queries and mutations that are Restricted to Cognito User Pool users, at least to work effortlessly, but for backend requests you would have to

so if you choose the IAM to keep the API restricted you would call a lambda to call de request from your backend, unless there's a way to sign the request from the remix backend with the AWS-SDK npm pkg.

pckilgore commented 2 years ago

Just as another vote, I don't have the time budget to re-implement the Amplify/Oauth stuff for the project I'm picking up remix to handle, it would be nice to have enough build system escape hatches for these kinds of things.

ErikCH commented 2 years ago

Hello @pckilgore and @misantronic !

I'm with the Amplify team, and with our latest release, I've been able to get Remix working with the Amplify library. We are still testing it, so if you find any issues let me know.

Here is our documentation.

https://ui.docs.amplify.aws/getting-started/installation

pckilgore commented 2 years ago

@ErikCH Ended up writing the server-side stuff with the SDK. Good to know for the future.

acusti commented 2 years ago

@ErikCH i’m super excited to hear that you’ve been able to get Remix working with Amplify! i would love to start exploring that myself. i checked the docs you linked to and it only covers the amplify UI library, whereas the part i’m stuck on is how to integrate Remix with Amplify SSR hosting. i read the AWS docs on Amplify SSR hosting, as well as the Amplify docs on Next.js hosting, and there are some parts that seem pretty Next.js specific, so i’m not sure how to adapt them to work with a Remix app.

the SSR docs give an example amplify.yml file:

version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: .next
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*

according to the docs, setting baseDirectory: .next indicates that the build artifacts are for a Next.js app that supports SSG and SSR pages (for SSG-only, it would be baseDirectory: out). what should that value be for a Remix app?

the docs also state that “Amplify inspects the app’s build script in the package.json file to detect whether the app is SSR or SSG”. the "build": "next build", indicates SSR, while ”build”: “next build && next export”, indicates SSG. do you know what that value should be for integrating with a Remix app?

thanks so much for any guidance you can provide! also, if you want me to take this discussion anywhere else (a different GitHub repo, the discord, etc.), i’m happy to do so. once i get Amplify and Remix working together, i will put together a write-up with guidance and instructions on my blog.

calavera commented 2 years ago

👋 @acusti It's currently not possible to deploy Remix apps using the Amplify Hosting SSR implementation because it's designed to work with Next.js at the moment. We're working on several improvements that would remove that limitation. Now that I'm aware of this thread, I'll keep you all informed here when we have news worth sharing.

CrshOverride commented 2 years ago

Hello @pckilgore and @misantronic !

I'm with the Amplify team, and with our latest release, I've been able to get Remix working with the Amplify library. We are still testing it, so if you find any issues let me know.

Here is our documentation.

https://ui.docs.amplify.aws/getting-started/installation

Do you happen to know if that includes the Authentication components? That's a big blocker for me at the moment moving forward.

ErikCH commented 2 years ago

@CrshOverride ! Yup that's what we've been testing, the Authenticator, and it's been going well.

CrshOverride commented 2 years ago

@ErikCH Do you have a gist or anything anywhere that you can toss up to show the flow? That would be amazing if it "just worked" but I need it to secure calls to the "backend" as well. :thinking:

github-actions[bot] commented 2 years ago

This issue has been automatically closed because we haven't received a response from the original author 🙈. This automation helps keep the issue tracker clean from issues that are unactionable. Please reach out if you have more information for us! 🙂

aaronksaunders commented 2 years ago

@ErikCH Do you have a gist or anything anywhere that you can toss up to show the flow? That would be amazing if it "just worked" but I need it to secure calls to the "backend" as well. 🤔

@CrshOverride - were you able to get any additional information about this? Thanks

CrshOverride commented 2 years ago

@aaronksaunders I wasn't. My naive attempts also didn't work at all. As soon as I added the Amplify UI components to my project, my bundle was too large to be deployed to Lambda.

aaronksaunders commented 2 years ago

Hmm, ok.

I have googled around and there doesn’t seem to be a solution other than a statement that it is being worked on and a link that doesn’t show the work.

I guess I will try the discord channel.

Thanks

On Fri, Apr 22, 2022 at 1:29 PM Justin Niessner @.***> wrote:

@aaronksaunders https://github.com/aaronksaunders I wasn't. My naive attempts also didn't work at all. As soon as I added the Amplify UI components to my project, my bundle was too large to be deployed to Lambda.

— Reply to this email directly, view it on GitHub https://github.com/remix-run/remix/issues/806#issuecomment-1106718362, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEAFGMJCBJN4CE5BVRJSYLVGLOXLANCNFSM5JB3L3DA . You are receiving this because you were mentioned.Message ID: @.***>

--

--

Aaron K. Saunders CEO Clearly Innovative Inc @.*** www.clearlyinnovative.com

This email message and any attachment(s) are for the sole use of the intended recipient(s) and may contain proprietary and/or confidential information which may be privileged or otherwise protected from disclosure. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient(s), please contact the sender by reply email and destroy the original message and any copies of the message as well as any attachment(s) to the original message.

ErikCH commented 2 years ago

Hi @aaronksaunders , @CrshOverride !

We don't have any official documentation on using Amplify with Remix yet. When I tested it, it was working. My testing was just limited to using the aws-amplify/ui-react package Authenticator. I followed the steps in our official documentation to create a new app. However, I didn't test every scenario, or hosting it somewhere in production, so your may run into some unexpected problems.

When we feel confident that we've tested enough scenarios, we'll add it to our official documentation. If you do run into problems though, as @aaronksaunders suggested, post on our Discord #ui-help channel. Or post your issue on our Github too.

@CrshOverride We made a fix that reduced the bundle size significantly recently. So you may want to try that again if the Lambda was having problems with the size.

acusti commented 2 years ago

@ErikCH i’ve been experimenting with migrating an existing amplify web app to remix (using cloudflare-workers as the server target) and am unable to get it to build. once i add aws-amplify as a dependency, i get this:

✘ [ERROR] Could not resolve "http2"

    node_modules/@aws-sdk/node-http-handler/dist/es/node-http2-handler.js:4:35:
      4 │ import { connect, constants } from "http2";
        ╵                                    ~~~~~~~

  The package "http2" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.

i then ran yarn why @aws-sdk/node-http-handler to see why that dependency is getting included:

[1/4] 🤔  Why do we have the module "@aws-sdk/node-http-handler"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=> Found "@aws-sdk/node-http-handler@3.6.1"
info Reasons this module exists
   - "@aws-amplify#core#@aws-sdk#client-cloudwatch-logs" depends on it
   - Hoisted from "@aws-amplify#core#@aws-sdk#client-cloudwatch-logs#@aws-sdk#node-http-handler"
   - Hoisted from "@aws-amplify#core#@aws-sdk#client-cognito-identity#@aws-sdk#node-http-handler"
   - Hoisted from "@aws-amplify#storage#@aws-sdk#client-s3#@aws-sdk#node-http-handler"
info Disk size without dependencies: "388KB"
info Disk size with unique dependencies: "1.41MB"
info Disk size with transitive dependencies: "1.54MB"
info Number of shared dependencies: 6

did you run into any issues from @aws-sdk/* packages and esbuild in your tests? any ideas on how i can work around them? i’m still unclear why these node.js-only libraries are being depended on from the @aws-amplify/* packages when they are intended to be used in a browser context, but i guess that’s a bigger question than this immediate issue.


a quick update: marking http2 as an external for esbuild results in that error going away and these fresh errors

✘ [ERROR] No matching export in "node-modules-polyfills:child_process" for import "exec"

    node_modules/@aws-sdk/credential-provider-process/dist/es/index.js:4:9:
      4 │ import { exec } from "child_process";
        ╵          ~~~~

 ✘ [ERROR] No matching export in "node-modules-polyfills:crypto" for import "createHash"

    node_modules/@aws-sdk/hash-node/dist/es/index.js:3:9:
      3 │ import { createHash, createHmac } from "crypto";
        ╵          ~~~~~~~~~~

 ✘ [ERROR] No matching export in "node-modules-polyfills:crypto" for import "createHmac"

    node_modules/@aws-sdk/hash-node/dist/es/index.js:3:21:
      3 │ import { createHash, createHmac } from "crypto";
        ╵                      ~~~~~~~~~~

 ✘ [ERROR] No matching export in "node-modules-polyfills:fs" for import "createReadStream"

    node_modules/@aws-sdk/hash-stream-node/dist/es/index.js:1:9:
      1 │ import { createReadStream } from "fs";
        ╵          ~~~~~~~~~~~~~~~~

 ✘ [ERROR] No matching export in "node-modules-polyfills:fs" for import "readFile"

    node_modules/@aws-sdk/shared-ini-file-loader/dist/es/index.js:2:9:
      2 │ import { readFile } from "fs";
        ╵          ~~~~~~~~

 ✘ [ERROR] No matching export in "node-modules-polyfills:os" for import "homedir"

    node_modules/@aws-sdk/shared-ini-file-loader/dist/es/index.js:3:9:
      3 │ import { homedir } from "os";
        ╵          ~~~~~~~

 ✘ [ERROR] No matching export in "node-modules-polyfills:fs" for import "lstatSync"

    node_modules/@aws-sdk/util-body-length-node/dist/es/index.js:1:9:
      1 │ import { lstatSync } from "fs";
        ╵          ~~~~~~~~~
pckilgore commented 2 years ago

You can try using just @aws-amplify/auth or @aws-amplify/ui-react.

I ended up implementing auth on the remix side with the v2 SDK.

aaronksaunders commented 2 years ago

I ended up getting it working with a combination of server side code and client side code using the Amplify Authenticator component to simplify the user management on the client and then passing the session information to the server to track in a session cookie. I then can make api calls using AppSync and the token from the user that was in the session cookie.

I do need to go back an add an additional call to verify the token on the server side but the basic structure is in the code sample

source code - https://github.com/aaronksaunders/amplify-remix-todos-1

CrshOverride commented 2 years ago

@aaronksaunders Which stack did you wind up using? I'm wondering if it'll address the issues I had my first attempt.

aaronksaunders commented 2 years ago

I wrote it from scratch. using the "Just the basics" option

On Mon, Jun 6, 2022 at 3:18 PM Justin Niessner @.***> wrote:

@aaronksaunders https://github.com/aaronksaunders Which stack did you wind up using? I'm wondering if it'll address the issues I had my first attempt.

— Reply to this email directly, view it on GitHub https://github.com/remix-run/remix/issues/806#issuecomment-1147808303, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEAFGIPAGK6C63OSQU6Y3DVNZFHJANCNFSM5JB3L3DA . You are receiving this because you were mentioned.Message ID: @.***>

--

--

Aaron K. Saunders CEO Clearly Innovative Inc @.*** www.clearlyinnovative.com

This email message and any attachment(s) are for the sole use of the intended recipient(s) and may contain proprietary and/or confidential information which may be privileged or otherwise protected from disclosure. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient(s), please contact the sender by reply email and destroy the original message and any copies of the message as well as any attachment(s) to the original message.

chrispepper1989 commented 1 year ago

Hey so I currently have a standard create react app hosted via aws amplify that does need to use the authentication.

I'm considering converting it to remix as a learning experience as well as giving the client the performance benefit.

But reading this thread I'm wondering is it worth the pain? And shall I save remix for my other project which will be hosted on cloudflare?

juliankrispel commented 1 year ago

Using amplify on the frontend kind of defeats the point of using remix :D - I will opt for the approach of implementing remix handlers for auth

j-fulbright commented 1 year ago

Has there been any changes to the SSR side to allow other libraries to work beyond Next?

MattyBalaam commented 1 year ago

Using amplify on the frontend kind of defeats the point of using remix :D - I will opt for the approach of implementing remix handlers for auth

You can use @aws-amplify/auth server side - although it is a little tricky.

silberistgold commented 9 months ago

Hi there,

I am currently trying to use the new amplify v6 apis in my remix app but I get stuck here:

    const keyValueStorage = createKeyValueStorageFromCookieStorageAdapter({
      get: async (name) => {
        const cookieHeader = request.headers.get("cookie");
        console.log("get", cookieHeader);
        const cookie = createCookie(name)
          .parse(cookieHeader)
          .then((c) => console.log("cc", c));
        console.log("cookie", cookie);
        return { name: "", value: "" };
      },
    });

Problem is that the get method is not allowed to be async and remix cookie api needs to be awaited. Anyone tried this and has ideas or made progress? Would also be nice to have a reference implementation for remix cookies like the one in the nextjs-adapter.

MattyBalaam commented 9 months ago

I experimented with the current Amplify 6 SSR solution, but as it stands currently you cannot officially sign in server side (and do not get things like IP address or user agent logged) so we have decided to write our own custom Cognito solution.

Someone from Amplify team claims that a proper server-side solution is in the works but there are no timescales for this:

Screenshot 2024-01-24 at 07 11 57

If this is not a blocker from you, then to get this working what you need to do is move grabbing the session outside:


const createKeyValueStorage = async (request) => {
  const values = await sessionStorage.getSession(request.headers.get('Cookie'))

  if (!values) throw new Error('No session found');

  return createKeyValueStorageFromCookieStorageAdapter({
    get: (name) => {

      const value = values.get(name)

      if (!value) {
        console.log({ name, values });

        throw new Error('No value found');
      }

      return { name, value };
    },

  });
};
Afsoon commented 8 months ago

@MattyBalaam do you have a full example to save the cookies using your approach? I'm trying but I can't successfully login. If I remove the SSR, keeping everything client side, everything works fine.

MattyBalaam commented 8 months ago

I would strongly recommend not using it! Are you on 5 or 6? There was one tricky workaround we needed to do which was that when you do the original sign-in we needed to store the temporary session (not user session) and then re-apply. It was quite frankly a mess.

Afsoon commented 8 months ago

@MattyBalaam I'm on v6

MattyBalaam commented 8 months ago

I can‘t help, for v6 I just threw something up just to test if if would run server-side as we wanted and the code is long gone.

Afsoon commented 8 months ago

@MattyBalaam thank you for answer, I just run out of ideas on how to make it work, the API on V6 it's very NextJS flavour, and I don't want move to v5 so probably we will do everything client side.