diff --git a/packages/cli/src/commands/setup/auth/auth.js b/packages/cli/src/commands/setup/auth/auth.js index eedaf7c9aa5f..46c094a1fed6 100644 --- a/packages/cli/src/commands/setup/auth/auth.js +++ b/packages/cli/src/commands/setup/auth/auth.js @@ -55,29 +55,6 @@ async function shouldForce(force, provider, webAuthn) { return false } -/** - * Prompt the user (unless already specified on the command line) if they want - * to enable WebAuthn support if they're setting up an auth service provider - * that has support for it. - * Right now, only dbAuth supports WebAuthn, but in theory it could work with - * any provider, so we'll do a check here and potentially use the webAuthn - * version of its provider - */ -async function shouldIncludeWebAuthn(webauthn, provider) { - if (webauthn === null && WEBAUTHN_SUPPORTED_PROVIDERS.includes(provider)) { - const webAuthnResponse = await prompts({ - type: 'confirm', - name: 'answer', - message: `Enable WebAuthn support (TouchID/FaceID)? See https://redwoodjs.com/docs/auth/dbAuth#webAuthn`, - initial: false, - }) - - return webAuthnResponse.answer - } - - return webauthn -} - export const command = 'auth ' export const description = 'Generate an auth configuration' export const builder = (yargs) => { diff --git a/packages/cli/src/commands/setup/auth/providers/dbAuth/setup.ts b/packages/cli/src/commands/setup/auth/providers/dbAuth/setup.ts new file mode 100644 index 000000000000..03bc5ec5b0a7 --- /dev/null +++ b/packages/cli/src/commands/setup/auth/providers/dbAuth/setup.ts @@ -0,0 +1,63 @@ +import prompts from 'prompts' +import terminalLink from 'terminal-link' +import yargs from 'yargs' +import { standardAuthHandler } from '../../setupHelpers' + +/** + * Prompt the user (unless already specified on the command line) if they want + * to enable WebAuthn support + */ +async function shouldIncludeWebAuthn(webauthn: boolean) { + if (webauthn === null) { + const webAuthnResponse = await prompts({ + type: 'confirm', + name: 'answer', + message: `Enable WebAuthn support (TouchID/FaceID)? See https://redwoodjs.com/docs/auth/dbAuth#webAuthn`, + initial: false, + }) + + return webAuthnResponse.answer + } + + return webauthn +} + +export const command = 'auth dbAuth' +export const description = 'Generate an auth configuration for dbAuth' +export const builder = (yargs: yargs.Argv) => { + yargs + .option('force', { + alias: 'f', + default: false, + description: 'Overwrite existing configuration', + type: 'boolean', + }) + .option('webauthn', { + alias: 'w', + default: null, + description: 'Include WebAuthn support (TouchID/FaceID)', + type: 'boolean', + }) + .epilogue( + `Also see the ${terminalLink( + 'Redwood CLI Reference', + 'https://redwoodjs.com/docs/cli-commands#setup-auth' + )}` + ) +} + +interface Args { + rwVersion: string + webauthn: boolean + force: boolean +} + +export const handler = async ({ rwVersion, webauthn, force: forceArg }: Args) => { + const webAuthn = await shouldIncludeWebAuthn(webauthn) + standardAuthHandler({ + rwVersion, + forceArg, + provider: 'dbAuth', + webAuthn, + }) +} diff --git a/packages/cli/src/commands/setup/auth/providers/netlify/netlify.js b/packages/cli/src/commands/setup/auth/providers/netlify/netlify.ts similarity index 100% rename from packages/cli/src/commands/setup/auth/providers/netlify/netlify.js rename to packages/cli/src/commands/setup/auth/providers/netlify/netlify.ts diff --git a/packages/cli/src/commands/setup/auth/providers/netlify/setup.ts b/packages/cli/src/commands/setup/auth/providers/netlify/setup.ts new file mode 100644 index 000000000000..0e928490211d --- /dev/null +++ b/packages/cli/src/commands/setup/auth/providers/netlify/setup.ts @@ -0,0 +1,22 @@ +import yargs from 'yargs' +import { standardAuthBuilder, standardAuthHandler } from '../../setupHelpers' + +export const command = 'auth netlify' +export const description = 'Generate an auth configuration for Netlify' +export const builder = (yargs: yargs.Argv) => { + return standardAuthBuilder(yargs) +} + +interface Args { + rwVersion: string + force: boolean +} + +export const handler = async ({ rwVersion, force: forceArg }: Args) => { + standardAuthHandler({ + rwVersion, + forceArg, + provider: 'netlify', + webAuthn: false, + }) +} diff --git a/packages/cli/src/commands/setup/auth/providers/supabase/setup.ts b/packages/cli/src/commands/setup/auth/providers/supabase/setup.ts new file mode 100644 index 000000000000..3bf684bbd5ed --- /dev/null +++ b/packages/cli/src/commands/setup/auth/providers/supabase/setup.ts @@ -0,0 +1,22 @@ +import yargs from 'yargs' +import { standardAuthBuilder, standardAuthHandler } from '../../setupHelpers' + +export const command = 'auth supabase' +export const description = 'Generate an auth configuration for Supabase' +export const builder = (yargs: yargs.Argv) => { + return standardAuthBuilder(yargs) +} + +interface Args { + rwVersion: string + force: boolean +} + +export const handler = async ({ rwVersion, force: forceArg }: Args) => { + standardAuthHandler({ + rwVersion, + forceArg, + provider: 'supabase', + webAuthn: false, + }) +} diff --git a/packages/cli/src/commands/setup/auth/providers/supabase/supabase.js b/packages/cli/src/commands/setup/auth/providers/supabase/supabase.ts similarity index 100% rename from packages/cli/src/commands/setup/auth/providers/supabase/supabase.js rename to packages/cli/src/commands/setup/auth/providers/supabase/supabase.ts diff --git a/packages/cli/src/commands/setup/auth/setupHelpers.ts b/packages/cli/src/commands/setup/auth/setupHelpers.ts new file mode 100644 index 000000000000..8b1dcfb31908 --- /dev/null +++ b/packages/cli/src/commands/setup/auth/setupHelpers.ts @@ -0,0 +1,129 @@ +import fs from 'fs' + +import Listr from 'listr' +import prompts from 'prompts' +import terminalLink from 'terminal-link' +import yargs from 'yargs' + +import { errorTelemetry } from '@redwoodjs/telemetry' + +import { getPaths } from '../../../lib' +import c from '../../../lib/colors' + +import { files } from './authFiles' +import { + addApiPackages, + addAuthConfigToGqlApi, + addAuthConfigToWeb, + addWebPackages, + generateAuthApi, + installPackages, + printNotes, +} from './authTasks' + +/** + * Check if one of the api side auth files already exists and if so, ask the + * user to overwrite + */ +async function shouldForce( + force: boolean, + provider: string, + webAuthn: boolean +) { + if (force) { + return true + } + + const existingFiles = Object.keys(files({ provider, webAuthn })).filter( + (filePath) => fs.existsSync(filePath) + ) + + if (existingFiles.length > 0) { + const shortPaths = existingFiles.map((filePath) => + filePath.replace(getPaths().base, '') + ) + const forceResponse = await prompts({ + type: 'confirm', + name: 'answer', + message: `Overwrite existing ${shortPaths.join(', ')}?`, + initial: false, + }) + + return forceResponse.answer + } + + return false +} + +export const standardAuthBuilder = (yargs: yargs.Argv) => { + yargs + .option('force', { + alias: 'f', + default: false, + description: 'Overwrite existing configuration', + type: 'boolean', + }) + .epilogue( + `Also see the ${terminalLink( + 'Redwood CLI Reference', + 'https://redwoodjs.com/docs/cli-commands#setup-auth' + )}` + ) +} + +interface Args { + rwVersion: string + forceArg: boolean + provider: string + webAuthn: boolean +} + +export const standardAuthHandler = async ({ + rwVersion, + forceArg, + provider, + webAuthn, +}: Args) => { + const force = await shouldForce(forceArg, provider, webAuthn) + const providerData = webAuthn + ? await import(`./providers/${provider}/${provider}.webAuthn`) + : await import(`./providers/${provider}/${provider}`) + + const tasks = new Listr( + [ + generateAuthApi(provider, force, webAuthn), + addAuthConfigToWeb(provider), + addAuthConfigToGqlApi, + addWebPackages(provider, providerData.webPackages, rwVersion), + addApiPackages(provider, providerData.apiPackages), + installPackages, + providerData.task, + printNotes(providerData.notes), + ].filter(Boolean), + // @ts-expect-error: This option is widely used, so I guess it works... + { collapse: false } + ) + + try { + await tasks.run() + } catch (e) { + if (isErrorWithMessage(e)) { + errorTelemetry(process.argv, e.message) + console.error(c.error(e.message)) + } + + if (isErrorWithErrorCode(e)) { + process.exit(e.exitCode || 1) + } else { + process.exit(1) + } + } +} + +function isErrorWithMessage(e: any): e is { message: string } { + return !!e.message +} + +function isErrorWithErrorCode(e: any): e is { exitCode: number } { + return !isNaN(e.exitCode) +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 000000000000..a5a33876f7cd --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.compilerOption.json", + "compilerOptions": { + "allowJs": true, + "baseUrl": ".", + "rootDir": "src", + "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo", + "outDir": "dist" + }, + "include": ["src"], + "references": [{ "path": "../telemetry" }] +} diff --git a/packages/telemetry/src/index.js b/packages/telemetry/src/index.ts similarity index 100% rename from packages/telemetry/src/index.js rename to packages/telemetry/src/index.ts diff --git a/packages/telemetry/tsconfig.json b/packages/telemetry/tsconfig.json new file mode 100644 index 000000000000..43947dd79bd1 --- /dev/null +++ b/packages/telemetry/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.compilerOption.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": "src", + "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo", + "outDir": "dist" + }, + "include": ["src"], +} diff --git a/yarn.lock b/yarn.lock index 344a6e0941ad..ebecf7599176 100644 --- a/yarn.lock +++ b/yarn.lock @@ -224,6 +224,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:7.18.10": + version: 7.18.10 + resolution: "@babel/core@npm:7.18.10" + dependencies: + "@ampproject/remapping": ^2.1.0 + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.18.10 + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-module-transforms": ^7.18.9 + "@babel/helpers": ^7.18.9 + "@babel/parser": ^7.18.10 + "@babel/template": ^7.18.10 + "@babel/traverse": ^7.18.10 + "@babel/types": ^7.18.10 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.1 + semver: ^6.3.0 + checksum: 94f749fb8bb844f5c5324513cac23edf781e24c15645a513eb54afc0e2f71d8c15c02e61f216a79f0f997deafc062b7c54bf2ebf31a2f62d0dd12bcbbc15dc97 + languageName: node + linkType: hard + "@babel/core@npm:7.18.13, @babel/core@npm:^7.1.0, @babel/core@npm:^7.11.1, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.12.9, @babel/core@npm:^7.13.16, @babel/core@npm:^7.14.0, @babel/core@npm:^7.7.5": version: 7.18.13 resolution: "@babel/core@npm:7.18.13" @@ -273,7 +296,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:7.18.13, @babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.14.0, @babel/generator@npm:^7.18.13, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:7.18.13, @babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.14.0, @babel/generator@npm:^7.18.10, @babel/generator@npm:^7.18.13, @babel/generator@npm:^7.7.2": version: 7.18.13 resolution: "@babel/generator@npm:7.18.13" dependencies: @@ -1741,6 +1764,16 @@ __metadata: languageName: node linkType: hard +"@babel/runtime-corejs3@npm:7.16.7": + version: 7.16.7 + resolution: "@babel/runtime-corejs3@npm:7.16.7" + dependencies: + core-js-pure: ^3.19.0 + regenerator-runtime: ^0.13.4 + checksum: ad94f88efd914e97ec02dd9071e518b07814c88b5e1a196b81aa5f09b79b64f5eb7de359039580388cf85ddd2efcf2f7487fe84ae97957da319a9a50220a45c6 + languageName: node + linkType: hard + "@babel/runtime-corejs3@npm:7.18.9, @babel/runtime-corejs3@npm:^7.10.2, @babel/runtime-corejs3@npm:^7.17.0": version: 7.18.9 resolution: "@babel/runtime-corejs3@npm:7.18.9" @@ -1771,7 +1804,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:7.18.13, @babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.18.11, @babel/traverse@npm:^7.18.13, @babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.7.2": +"@babel/traverse@npm:7.18.13, @babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.18.10, @babel/traverse@npm:^7.18.11, @babel/traverse@npm:^7.18.13, @babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.7.2": version: 7.18.13 resolution: "@babel/traverse@npm:7.18.13" dependencies: @@ -6151,6 +6184,49 @@ __metadata: languageName: unknown linkType: soft +"@redwoodjs/auth-providers@workspace:packages/auth-providers": + version: 0.0.0-use.local + resolution: "@redwoodjs/auth-providers@workspace:packages/auth-providers" + dependencies: + "@auth0/auth0-spa-js": 1.22.2 + "@azure/msal-browser": 2.28.1 + "@babel/cli": 7.18.10 + "@babel/core": 7.18.10 + "@babel/runtime-corejs3": 7.18.9 + "@clerk/clerk-js": 3.17.0 + "@clerk/clerk-react": 3.5.1 + "@clerk/clerk-sdk-node": 3.9.2 + "@clerk/types": 2.21.0 + "@nhost/hasura-auth-js": 1.4.1 + "@nhost/nhost-js": 1.4.8 + "@okta/okta-auth-js": 6.7.6 + "@redwoodjs/auth": 2.2.0 + "@simplewebauthn/browser": 5.4.1 + "@simplewebauthn/typescript-types": 5.4.0 + "@supabase/supabase-js": 1.35.6 + "@types/netlify-identity-widget": 1.9.3 + "@types/react": 17.0.48 + core-js: 3.24.1 + firebase: 9.9.2 + firebase-admin: 10.3.0 + gotrue-js: 0.9.29 + jest: 28.1.3 + magic-sdk: 9.0.0 + netlify-identity-widget: 1.9.2 + react: 17.0.2 + supertokens-auth-react: 0.24.4 + typescript: 4.7.4 + peerDependencies: + "@clerk/clerk-react": 3.5.1 + "@clerk/clerk-sdk-node": 3.9.2 + peerDependenciesMeta: + "@clerk/clerk-react": + optional: true + "@clerk/clerk-sdk-node": + optional: true + languageName: unknown + linkType: soft + "@redwoodjs/auth@2.2.2, @redwoodjs/auth@workspace:packages/auth": version: 0.0.0-use.local resolution: "@redwoodjs/auth@workspace:packages/auth" @@ -6185,6 +6261,16 @@ __metadata: languageName: unknown linkType: soft +"@redwoodjs/auth@npm:2.2.0": + version: 2.2.0 + resolution: "@redwoodjs/auth@npm:2.2.0" + dependencies: + "@babel/runtime-corejs3": 7.16.7 + core-js: 3.23.5 + checksum: d6b8ca81b25c0dc28045f7f86a597ec36c7748cab8d7fcfd7beecc833e4d102231bea7a7b06056f2d449dd4d6f07d4bb49c0073690e31ba6e4a50f12426f8c22 + languageName: node + linkType: hard + "@redwoodjs/cli@2.2.2, @redwoodjs/cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@redwoodjs/cli@workspace:packages/cli" @@ -13326,6 +13412,13 @@ __metadata: languageName: node linkType: hard +"core-js-pure@npm:^3.19.0": + version: 3.25.0 + resolution: "core-js-pure@npm:3.25.0" + checksum: 987639f66d06ca514fa33a0691231d31bf834dd4521c659be20dea7d4c23edb4b4e5305d78f3b45c3221c3041ea770e80f14cfddde191e4bc5ef18a87cae90a0 + languageName: node + linkType: hard + "core-js-pure@npm:^3.20.2, core-js-pure@npm:^3.8.1": version: 3.21.0 resolution: "core-js-pure@npm:3.21.0" @@ -13333,6 +13426,13 @@ __metadata: languageName: node linkType: hard +"core-js@npm:3.23.5": + version: 3.23.5 + resolution: "core-js@npm:3.23.5" + checksum: ab9b483ede285b3949e2411f9190d147ac589d111b0aa3a4b2999a1bf2755b3e930529c2a22ff550efd4fc154c7cfae700bee858dedff905a09f35ddbcc6425f + languageName: node + linkType: hard + "core-js@npm:3.24.1, core-js@npm:^3.0.4, core-js@npm:^3.18.3, core-js@npm:^3.22.1, core-js@npm:^3.23.2, core-js@npm:^3.6.5, core-js@npm:^3.8.2": version: 3.24.1 resolution: "core-js@npm:3.24.1"