timothymiller / t4-app

Full stack meta-framework for building iOS, Android, and Progressive Web Apps with Tamagui and deploying to Cloudflare.
https://t4stack.com
MIT License
1.5k stars 84 forks source link

Lucia branch: ERROR: Failed to produce a Cloudflare Pages build from the project. The route `/oauth/[provider]` was not configured to run with the Edge Runtime #129

Open Boscop opened 11 months ago

Boscop commented 11 months ago

When I run the CF-deploy GH action https://github.com/timothymiller/t4-app/blob/325b5e58a39bf9cd4019db6273ad6ce62889cf2d/.github/workflows/next.yml#L46 I get this error:

⚡️ Completed bunx vercel build.

⚡️ ERROR: Failed to produce a Cloudflare Pages build from the project. ⚡️ ⚡️ The following routes were not configured to run with the Edge Runtime: ⚡️ - /oauth/[provider] ⚡️ ⚡️ Please make sure that all your non-static routes export the following edge runtime route segment config: ⚡️ export const runtime = 'edge'; ⚡️ ⚡️ You can read more about the Edge Runtime on the Next.js documentation: ⚡️ https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes

Any idea why? It didn't happen when I tried deploying the master branch a month ago..

Boscop commented 11 months ago

It seems to work when I add export const runtime = 'experimental-edge'; to apps/next/pages/oauth/[provider].tsx.

Is this really necessary / is this the right solution?

Why did it work without it for you before? (I.e. why didn't you have this issue so far when deploying the lucia branch?)

timothymiller commented 11 months ago

I've been trying to track this issue down this past week.

Does it work when you set the runtime to 'experimental-edge'?

timothymiller commented 11 months ago

The long term solution is going to be moving this server side code to an API endpoint, outside of next.js

Boscop commented 11 months ago

Does it work when you set the runtime to 'experimental-edge'?

Yes (like I wrote above in my reply).

I'm just not sure if I should set the runtime to 'experimental-edge' only for this route or for all (in apps/next/next.config.js)..

rmarscher commented 11 months ago

Sorry about this. I recently added handling apple oauth form posts and apparently never tested deploying to cloudflare. I must have only been running it locally.

I added some comments to the code to explain what it is doing there. Apple will POST form data to the redirect URI when scopes have been requested. https://developer.apple.com/documentation/sign_in_with_apple/request_an_authorization_to_the_sign_in_with_apple_server

timothymiller commented 11 months ago

Does it work when you set the runtime to 'experimental-edge'?

Yes (like I wrote above in my reply).

I'm just not sure if I should set the runtime to 'experimental-edge' only for this route or for all (in apps/next/next.config.js)..

Do you have a deployed example we can test?

When I've deployed this with experimental-edge set, I receive this error server side: "Error: Some functionality, such as asynchronous I/O, timeouts, and generating random values, can only be performed while handling a request."

Boscop commented 11 months ago

Does it work when you set the runtime to 'experimental-edge'?

Yes (like I wrote above in my reply). I'm just not sure if I should set the runtime to 'experimental-edge' only for this route or for all (in apps/next/next.config.js)..

Do you have a deployed example we can test?

When I've deployed this with experimental-edge set, I receive this error server side: "Error: Some functionality, such as asynchronous I/O, timeouts, and generating random values, can only be performed while handling a request."

I literally just appended export const runtime = 'experimental-edge'; at the bottom of apps/next/pages/oauth/[provider].tsx and it seems to be working. (I didn't change the config in apps/next/next.config.js.) Where do you see this server-side error btw? :)

timothymiller commented 11 months ago

When signing in with Apple, the redirect will take you to /oauth/apple with some query params. It's on this route that the client displays "internal server error", and if you check the cloudflare logs, you will see the above error.

After I removed the Tamagui provider and components from the route, the getServerSideProps call was able to complete.

timothymiller commented 11 months ago

Btw, I'm a big fan of your work on rust bindings for webview 😃

Boscop commented 11 months ago

Ah. I didn't setup any oauth providers yet, I only tested with email auth so far. (And with that, looking at the log stream under Real-time Logs in my worker, I only see successful responses.)

timothymiller commented 11 months ago

Email works well. Lmk how signing in with apple works for you.

If someone else can reproduce this error, I'll do a PR for moving the oauth provider logic to an api route.

rmarscher commented 11 months ago

Running in the iOS simulator on my #136 branch, I get

email: "myemail@gmail.com"
email_verified: "true"

in the parsed ID token payload. For the use case of the current simple auth, that is enough. So maybe implementing the apple form_post handler isn't that important.

timothymiller commented 11 months ago

Cool! So maybe we can do the getServerSideProps code client side?

rmarscher commented 11 months ago

It already does most of the processing on the client side. The server code is just for making the POST data available somehow for the client. The middleware.ts function works fine with cloudflare so that's another option instead of a nextjs pages api route. It could redirect the browser and include json encoded apple user data in the browser url hash and then the client could grab it and rewrite browser history to take it out. That leaks that user data a bit by sending it in a url request though. A more secure option would be to create a new d1 table to hold authorization code <> user data pairs with an expiration time and write to that from the server when it receives the post data. And then when the client calls sign in, the api server can look up the authorization code in the db and get the additional user data and delete it out of that table. A clean up process would need to be run once in a while to clean up old data in that table. A kv store with expiration or something like that would also work.

timothymiller commented 11 months ago

I like the idea of handling it in middleware and returning the value as a cookie that can be deleted once processed client side