sidebase / nuxt-auth

Authentication built for Nuxt 3! Easily add authentication via OAuth providers, credentials or Email Magic URLs!
https://auth.sidebase.io
MIT License
1.32k stars 164 forks source link

CloudFlare Pages deployment fails with RollupError #378

Closed yu-kani closed 1 year ago

yu-kani commented 1 year ago

Environment

Reproduction

No response

Describe the bug

During the deployment process on CloudFlare Pages,an error is occurring because the jose package is unable to use the createHash function from the unenv package it depends on. Specifically, the digest.js file in the jose package is importing the createHash function from the crypto package, but it needs to retrieve createHash from the crypto module of the unenv package, which is not properly exported.

Additional context

I have tried the following and would like to report an issue:

Logs

08:45:06.136    [info] [nitro] Building Nitro Server (preset: `cloudflare-pages`)
08:45:48.545    [error] [nitro] RollupError: "createHash" is not exported by "node_modules/unenv/runtime/node/crypto/index.mjs", imported by "node_modules/openid-client/node_modules/jose/dist/node/esm/runtime/digest.js".
08:45:48.546    
08:45:48.546    
08:45:48.546    1: import { createHash } from 'crypto';
08:45:48.546                ^
08:45:48.546    2: const digest = (algorithm, data) => createHash(algorithm).update(data).digest();
08:45:48.546    3: export default digest;
08:45:48.548    [error] "createHash" is not exported by "node_modules/unenv/runtime/node/crypto/index.mjs", imported by "node_modules/openid-client/node_modules/jose/dist/node/esm/runtime/digest.js".
08:45:48.549      at error (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:2128:30)
08:45:48.549      at Module.error (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:13322:16)
08:45:48.549      at Module.traceVariable (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:13707:29)
08:45:48.549      at ModuleScope.findVariable (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:12210:39)
08:45:48.549      at ReturnValueScope.findVariable (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:6953:38)
08:45:48.549      at Identifier.bind (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:8103:40)
08:45:48.550      at CallExpression.bind (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:5749:23)
08:45:48.550      at CallExpression.bind (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:9630:15)
08:45:48.550      at MemberExpression.bind (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:5749:23)
08:45:48.550      at MemberExpression.bind (node_modules/nuxt/node_modules/rollup/dist/es/shared/node-entry.js:9291:19)
08:45:48.701    error Command failed with exit code 1.
08:45:48.702    info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
08:45:48.727    Failed: build command exited with code: 1
08:45:49.934    Failed: error occurred while running build command
Jaimeer commented 1 year ago

+1

BracketJohn commented 1 year ago

Hey @yu-kani 👋

Thanks for reporting:

I think this rather related to authjs / next-auth as they depend on jose, see https://github.com/nextauthjs/next-auth/blob/main/packages/core/package.json -> so you'd have to file the problem in their repo / look into their repo to see if they already have a similar issue.

yu-kani commented 1 year ago

Hey @BracketJohn

Thank you for your assistance. I have prepared a repository where the error can be reproduced. The reproduction steps are as follows:

Repository where the error is reproduced

https://github.com/yu-kani/nuxt-auth-test

Pattern 1

  1. Clone the repository to your local machine.
  2. Run yarn build.

Pattern 2

  1. Publish the Git repository to Cloudflare Pages.
  2. Set the environment variable NODE_VERSION = v18.16.0 in Cloudflare Pages.
  3. Deploy the repository.
  4. The error can be confirmed.

I am considering whether the cause lies in nuxt-auth or jose, but I do not have detailed knowledge and therefore do not know for sure. I am sorry.

cliqer commented 1 year ago

@BracketJohn I solved a similar error with cloudflare workers crypto compatibility. Maybe it gives you some ideas on how to fix:


import {isDevelopment} from "std-env";

if (isDevelopment && typeof window === 'undefined') {
    import('crypto').then(crypto => globalThis.crypto = crypto);
}

export default defineEventHandler(async event => {
    const {uid} = getQuery(event);

    const sharedSecret = 'a-secure-password';
    const turnServer = 'turn.example.com';
    const ttl = 86400;
    const username = Math.floor(Date.now() / 1000) + ttl
    const encoder = new TextEncoder();
    const data = encoder.encode(username.toString());
    const keyData = encoder.encode(sharedSecret);

    const key = await crypto?.subtle?.importKey(
        'raw',
        keyData,
        { name: 'HMAC', hash: 'SHA-1' },
        false,
        ['sign']
    );

    const signature = await crypto?.subtle.sign('HMAC', key, data);
    const password = Buffer.from(signature).toString('base64');
    // const password = base64url.encode(Buffer.from(new Uint8Array(signature)));

    return {
        username,
        password,
        ttl
    };
})
jharris-tc commented 1 year ago

I had this same issue in my nuxt deployment.

I am not a javascript expert, but from looking at the dependencies it seems that jose is used by next-auth, which is a peer dependency with nuxt-auth. unenv, which seems to be a polyfilling library, is used by nitropack, which is used by nuxt-auth. unenv has now polyfilled the crypto library, but without the createHash and createHMAC functions.

My solution was just to pin unenv to 1.2.2, which is the version before it added crypto support.

I am not sure if there is a way to make a PR to unenv to like pass on the functions it doesnt polyfil, but I do not know enough about node or JS to do that.

TL;DR -> hard pin/override unenv to be version 1.2.2

yu-kani commented 1 year ago

@jharris-tc thanks for the investigation.

I'm still not getting a successful build myself.

Environment

package

yarn add unenv@1.2.2

Result

yarn build 15:05:25.377 [info] [nitro] Building Nitro Server (preset: cloudflare-pages)
15:05:26.829 [error] [nitro] Error: Could not load /opt/buildhome/repo/node_modules/unenv/runtime/node/crypto/index.mjs (imported by node_modules/uncrypto/dist/crypto.node.mjs): ENOENT: no such file or directory, open '/opt/buildhome/repo/node_modules/unenv/runtime/node/crypto/index.mjs'
15:05:26.829  
15:05:26.830  
15:05:26.830 undefined
15:05:26.830 [error] Could not load /opt/buildhome/repo/node_modules/unenv/runtime/node/crypto/index.mjs (imported by node_modules/uncrypto/dist/crypto.node.mjs): ENOENT: no such file or directory, open '/opt/buildhome/repo/node_modules/unenv/runtime/node/crypto/index.mjs'
15:05:26.830  
15:05:26.873 error Command failed with exit code 1.
15:05:26.873 info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
15:05:26.900 Failed: build command exited with code: 1
15:05:27.832 Failed: error occurred while running build command
gritworks commented 1 year ago

I had this same issue in my nuxt deployment.

I am not a javascript expert, but from looking at the dependencies it seems that jose is used by next-auth, which is a peer dependency with nuxt-auth. unenv, which seems to be a polyfilling library, is used by nitropack, which is used by nuxt-auth. unenv has now polyfilled the crypto library, but without the createHash and createHMAC functions.

My solution was just to pin unenv to 1.2.2, which is the version before it added crypto support.

I am not sure if there is a way to make a PR to unenv to like pass on the functions it doesnt polyfil, but I do not know enough about node or JS to do that.

TL

and indeed looking at unenv the sourcecode says: (https://github.com/unjs/unenv/blob/main/src/runtime/node/crypto/index.ts)

// TODO: Add missing exports (typecheck is not working!) export default { randomUUID, getRandomValues, randomBytes, subtle, webcrypto, };

This is not an issue with nuxt-auth itself. hope this gets fixed soon.

jharris-tc commented 1 year ago

here is my package.json

{
  "private": true,
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
  "devDependencies": {
    "@nuxt/devtools": "^0.4.6",
    "@pinia-plugin-persistedstate/nuxt": "^1.1.1",
    "@sidebase/nuxt-auth": "^0.4.0",
    "@stripe/stripe-js": "^1.54.1",
    "nuxt": "^3.1.2",
    "vue-stripe-js": "^1.0.1"
  },
  "dependencies": {
    "@aws-amplify/ui-vue": "^3.1.7",
    "@nuxtjs/axios": "^5.13.6",
    "@pinia/nuxt": "^0.4.8",
    "@tucowsinc/vue-ui-styling-dxp": "^0.0.2",
    "@vee-validate/nuxt": "^4.9.5",
    "aws-amplify": "^5.0.14",
    "less": "^4.1.3",
    "pinia": "^2.0.34"
  },
  "overrides": {
    "@sidebase/nuxt-auth": {
      "unenv": "1.2.2"
    }
  }
}

hopefully this helps, I just ran it again with npm clean-install --progress=false -> npm run build -> the publish action via GHA (this also worked using the cloudflare pages dashboard, but we moved it out of there to have better visibility of logs) and it was fine

seems like your versions might be ahead of mine, hope you can figure it out

gritworks commented 1 year ago

ah, thanks. i searched my eyes out trying to understand the overrides. I think this is also needed (for my setup and maybe for others) to override it for nitropack (also using crypto)

-- edit --

from bad to worse i guess. this spawns a heap of new errors. and eventually the build fails with:

Error: Failed to publish your Function. Got error: Uncaught TypeError: Object prototype may only be an Object or null: undefined at functionsWorker-0.8747573045606203.js:5837:41 in Ir.exports at functionsWorker-0.8747573045606203.js:7375:4

i guess jose depends on more features implemented in newer version of unenv:

node_modules/jose/dist/node/esm/runtime/is_key_object.js (3:20) "types" is not exported by "node_modules/unenv/runtime/node/util/index.mjs", imported by "node_modules/jose/dist/node/esm/runtime/is_key_object.js".

the quickest fix for this would be unenv completing the missing crypto functions like createHash.

thanks for the effort though, its appreciated.

AwesomeDude091 commented 1 year ago

Can confirm this is an issue

jharris-tc commented 1 year ago

ah, thanks. i searched my eyes out trying to understand the overrides. I think this is also needed (for my setup and maybe for others) to override it for nitropack (also using crypto)

-- edit --

from bad to worse i guess. this spawns a heap of new errors. and eventually the build fails with:

Error: Failed to publish your Function. Got error: Uncaught TypeError: Object prototype may only be an Object or null: undefined at functionsWorker-0.8747573045606203.js:5837:41 in Ir.exports at functionsWorker-0.8747573045606203.js:7375:4

i guess jose depends on more features implemented in newer version of unenv:

node_modules/jose/dist/node/esm/runtime/is_key_object.js (3:20) "types" is not exported by "node_modules/unenv/runtime/node/util/index.mjs", imported by "node_modules/jose/dist/node/esm/runtime/is_key_object.js".

the quickest fix for this would be unenv completing the missing crypto functions like createHash.

thanks for the effort though, its appreciated.

I get the types is not exported, unenv -> esm too, but it seems okay? I agree the cleanest fix is to unenv

AwesomeDude091 commented 1 year ago

Until Unenv fixes it there end is there a temporary fix that could be implemented?

Hebilicious commented 1 year ago

This happens because next-auth is not really compatible with cloudflare workers out of the box. For anyone that has this issue and need a working solution, I created a different module that integrates directly with auth.js, in a more straightforward way. It's significantly simpler in scope compared to sidebase, and I'm using it for Cloudflare Pages / Cloudflare Workers without any issues.

I've also documented some of the workaround that you need to fix some of the nitro/unenv build issues that you'll have due to authjs/pnpm/unenv

https://github.com/Hebilicious/authjs-nuxt

Maybe the sidebase team is interested into using this approach for a v1 ?

Otoris commented 1 year ago

Still getting this as well. Considering switching to @Hebilicious repo but that's a ton of work 😢 .

Hebilicious commented 1 year ago

Still getting this as well. Considering switching to @Hebilicious repo but that's a ton of work 😢 .

What features do you need that are missing ?

Otoris commented 1 year ago

Still getting this as well. Considering switching to @Hebilicious repo but that's a ton of work 😢 .

What features do you need that are missing ?

I don't think anything is missing - just complaining it is a lot of work to switch libraries. I just put together a proof of concept to investigate however! I left an issue on your repo that is a potential blocker for me, but maybe you've run into this before. https://github.com/Hebilicious/authjs-nuxt/issues/43

Hebilicious commented 1 year ago

@Otoris Thanks, just pushed a fix for this issue.

theleaks commented 1 year ago

ah, thanks. i searched my eyes out trying to understand the overrides. I think this is also needed (for my setup and maybe for others) to override it for nitropack (also using crypto)

-- edit --

from bad to worse i guess. this spawns a heap of new errors. and eventually the build fails with:

Error: Failed to publish your Function. Got error: Uncaught TypeError: Object prototype may only be an Object or null: undefined at functionsWorker-0.8747573045606203.js:5837:41 in Ir.exports at functionsWorker-0.8747573045606203.js:7375:4

i guess jose depends on more features implemented in newer version of unenv:

node_modules/jose/dist/node/esm/runtime/is_key_object.js (3:20) "types" is not exported by "node_modules/unenv/runtime/node/util/index.mjs", imported by "node_modules/jose/dist/node/esm/runtime/is_key_object.js".

the quickest fix for this would be unenv completing the missing crypto functions like createHash.

thanks for the effort though, its appreciated.

I'm having this problem when trying to build cloudflare for Nuxt.

When I import the ethers package, I have a problem because of the crypto package.

@ethersproject/providers I tried to use it, this time I got the same error as yours. How can we solve the problem? I want to use ethers but I can't deploy to cloudflare.

AwesomeDude091 commented 1 year ago

You have to either migrate away from dependencies that require inbuilt node libraries (i.e. node:crypto) or you check which inbuilt node libraries are the root of the failed build (for Sidebase it is Node Crypto) and then hope that a polyfill is developed for that library. The gist of the error is that a dependency requires an inbuilt Node JS library, which is not present in a edge runtime environment such as Cloudflare Pages. Somebody then has to stich together a bridge between the NodeJS library and the edge environments (polyfill).

Here is an incomplete polyfill of the Node Crypto library being worked on in the unenv project. https://github.com/unjs/unenv/pull/111

zoey-kaiser commented 1 year ago

In version 0.6, we introduced the local providor, this should (did not test it myself) work with cloudflare workers. Otherwise, it is now also possible for the community to use NuxtAuth as a framework to write their own providors, which would allow someone in the community to also write their own support. (See https://github.com/sidebase/nuxt-auth/pull/495 as an example)

I will go ahead and close this issue for now, as I don't see our team being able to futhur tackle this deployment issue. If an issue arrises with deploying the local providor to cloudflare, feel free to open a new issue and we can discuss it there!

AwesomeDude091 commented 1 year ago

Would this mean that there is compatibility for using OAuth2 Providers?

zoey-kaiser commented 1 year ago

Would this mean that there is compatibility for using OAuth2 Providers?

As mentioned by @Hebilicious, NextAuth, the OAuth package we use under the hood, does not support cloudflare workers, which means that with out current implementation, there will be no support for them, until NextAuth adds support. Sadly this means we are bound by their implementation and cannot overwrite or change this, unless we completly move away from their systems.

AwesomeDude091 commented 1 year ago

Is there any plans to do that?

zoey-kaiser commented 1 year ago

Is there any plans to do that?

With version 0.6, we do open the doors, to allow this, however re-implementing every oAuth provider, that NextAuth has, will take its time. We hope to be able to make NuxtAuth more extendable for others, that you could easily add the configuration for the oAuth provider you wish to use.

But first, my goal is to iron out the last issues with 0.6 and get it released. Once we did this, I can discuss with the team what we could add to the roadmap. However, I do understand, this would be nice to add and have added it to my list!