From aabc7f67b2a17291d8e36575604d53b3ce058627 Mon Sep 17 00:00:00 2001 From: Mohamed Melouk <42706279+cobraprojects@users.noreply.github.com> Date: Mon, 11 May 2026 06:52:10 +0300 Subject: [PATCH 1/3] add workos logic --- .../app/api/auth/workos/callback/route.ts | 10 + .../app/api/auth/workos/login/route.ts | 5 + .../app/api/auth/workos/logout/route.ts | 10 + .../app/api/auth/workos/register/route.ts | 5 + apps/blog-next/app/auth-nav.tsx | 7 + apps/blog-next/app/login/page.tsx | 3 +- apps/blog-next/app/register/page.tsx | 1 + apps/blog-next/config/auth.ts | 4 +- apps/blog-nuxt/app/app.vue | 6 + .../app/pages/{login.vue => login/index.vue} | 1 + .../{register.vue => register/index.vue} | 1 + apps/blog-nuxt/config/auth.ts | 4 +- .../server/api/auth/workos/callback.get.ts | 11 + .../server/api/auth/workos/login.get.ts | 5 + .../server/api/auth/workos/logout.post.ts | 14 + .../server/api/auth/workos/register.get.ts | 5 + apps/blog-sveltekit/config/auth.ts | 4 +- apps/blog-sveltekit/src/routes/+layout.svelte | 6 + .../api/auth/workos/callback/+server.ts | 11 + .../routes/api/auth/workos/login/+server.ts | 6 + .../routes/api/auth/workos/logout/+server.ts | 11 + .../api/auth/workos/register/+server.ts | 6 + .../src/routes/login/+page.svelte | 1 + .../src/routes/register/+page.svelte | 1 + apps/docs/docs/auth/workos.md | 190 ++--- packages/auth-workos/src/contracts.ts | 57 +- packages/auth-workos/src/index.ts | 804 ++++++++++++++++-- packages/auth-workos/tests/package.test.ts | 247 +++++- packages/auth/src/runtime/responseCookies.ts | 6 +- packages/auth/tests/docs-smoke.test.ts | 4 +- packages/auth/tests/package.test.ts | 8 +- .../src/project/scaffold/config-renderers.ts | 4 +- .../cli/src/project/scaffold/dependencies.ts | 4 +- .../src/project/scaffold/project-renderers.ts | 4 +- packages/cli/tests/cli.test.ts | 4 +- packages/config/src/defaults.ts | 74 +- packages/config/src/loader.ts | 4 +- packages/config/src/types.ts | 17 +- packages/config/tests/config.test.ts | 44 +- packages/config/tests/config.type.test.ts | 4 +- packages/core/src/portable/holo.ts | 4 +- packages/core/tests/auth-runtime.test.ts | 33 +- tests/example-app-auth-flow.mjs | 6 + 43 files changed, 1355 insertions(+), 301 deletions(-) create mode 100644 apps/blog-next/app/api/auth/workos/callback/route.ts create mode 100644 apps/blog-next/app/api/auth/workos/login/route.ts create mode 100644 apps/blog-next/app/api/auth/workos/logout/route.ts create mode 100644 apps/blog-next/app/api/auth/workos/register/route.ts rename apps/blog-nuxt/app/pages/{login.vue => login/index.vue} (97%) rename apps/blog-nuxt/app/pages/{register.vue => register/index.vue} (97%) create mode 100644 apps/blog-nuxt/server/api/auth/workos/callback.get.ts create mode 100644 apps/blog-nuxt/server/api/auth/workos/login.get.ts create mode 100644 apps/blog-nuxt/server/api/auth/workos/logout.post.ts create mode 100644 apps/blog-nuxt/server/api/auth/workos/register.get.ts create mode 100644 apps/blog-sveltekit/src/routes/api/auth/workos/callback/+server.ts create mode 100644 apps/blog-sveltekit/src/routes/api/auth/workos/login/+server.ts create mode 100644 apps/blog-sveltekit/src/routes/api/auth/workos/logout/+server.ts create mode 100644 apps/blog-sveltekit/src/routes/api/auth/workos/register/+server.ts diff --git a/apps/blog-next/app/api/auth/workos/callback/route.ts b/apps/blog-next/app/api/auth/workos/callback/route.ts new file mode 100644 index 00000000..ac9510e8 --- /dev/null +++ b/apps/blog-next/app/api/auth/workos/callback/route.ts @@ -0,0 +1,10 @@ +import { completeWorkosAuth } from '@holo-js/auth-workos' + +export async function GET(request: Request) { + const result = await completeWorkosAuth(request) + if (!result.ok) { + return Response.redirect(new URL(`/login?error=${encodeURIComponent(result.code)}`, request.url)) + } + + return Response.redirect(new URL('/admin', request.url)) +} diff --git a/apps/blog-next/app/api/auth/workos/login/route.ts b/apps/blog-next/app/api/auth/workos/login/route.ts new file mode 100644 index 00000000..3839061c --- /dev/null +++ b/apps/blog-next/app/api/auth/workos/login/route.ts @@ -0,0 +1,5 @@ +import { loginWithWorkos } from '@holo-js/auth-workos' + +export async function GET(request: Request) { + return await loginWithWorkos(request) +} diff --git a/apps/blog-next/app/api/auth/workos/logout/route.ts b/apps/blog-next/app/api/auth/workos/logout/route.ts new file mode 100644 index 00000000..d556de00 --- /dev/null +++ b/apps/blog-next/app/api/auth/workos/logout/route.ts @@ -0,0 +1,10 @@ +import { logoutWithWorkos } from '@holo-js/auth-workos' + +export async function POST(request: Request) { + const result = await logoutWithWorkos(request) + if (!result.ok) { + return Response.json(result, { status: 422 }) + } + + return Response.redirect(result.url, 303) +} diff --git a/apps/blog-next/app/api/auth/workos/register/route.ts b/apps/blog-next/app/api/auth/workos/register/route.ts new file mode 100644 index 00000000..93ace66f --- /dev/null +++ b/apps/blog-next/app/api/auth/workos/register/route.ts @@ -0,0 +1,5 @@ +import { registerWithWorkos } from '@holo-js/auth-workos' + +export async function GET(request: Request) { + return await registerWithWorkos(request) +} diff --git a/apps/blog-next/app/auth-nav.tsx b/apps/blog-next/app/auth-nav.tsx index 98ce7f72..61dcd30d 100644 --- a/apps/blog-next/app/auth-nav.tsx +++ b/apps/blog-next/app/auth-nav.tsx @@ -18,6 +18,10 @@ const logoutButtonStyle = { padding: 0, } as const +const logoutFormStyle = { + display: 'inline', +} as const + export function AuthNav() { const auth = useAuth() const [isLoggingOut, setIsLoggingOut] = useState(false) @@ -57,6 +61,9 @@ export function AuthNav() { <> {displayName} +
+ +
) } diff --git a/apps/blog-next/app/login/page.tsx b/apps/blog-next/app/login/page.tsx index 2c396842..5b5c7f2d 100644 --- a/apps/blog-next/app/login/page.tsx +++ b/apps/blog-next/app/login/page.tsx @@ -47,6 +47,7 @@ export default function LoginPage() {
Continue with Google Continue with GitHub + Continue with WorkOS
{ event.preventDefault(); form.submit() }} style={{ display: 'grid', gap: '0.9rem' }}> @@ -97,7 +98,7 @@ export default function LoginPage() { ) : null}
- Create account + Create account Forgot password?
diff --git a/apps/blog-next/app/register/page.tsx b/apps/blog-next/app/register/page.tsx index 1a744cf5..168396d9 100644 --- a/apps/blog-next/app/register/page.tsx +++ b/apps/blog-next/app/register/page.tsx @@ -100,6 +100,7 @@ export default function RegisterPage() { ) : null} Already have an account? + Register with WorkOS ) } diff --git a/apps/blog-next/config/auth.ts b/apps/blog-next/config/auth.ts index 2c9f962b..b54d336e 100644 --- a/apps/blog-next/config/auth.ts +++ b/apps/blog-next/config/auth.ts @@ -41,7 +41,6 @@ export default defineAuthConfig({ personalAccessTokens: { defaultAbilities: [], }, - socialEncryptionKey: env('AUTH_SOCIAL_ENCRYPTION_KEY'), social: { google: { clientId: env('AUTH_GOOGLE_CLIENT_ID'), @@ -57,12 +56,11 @@ export default defineAuthConfig({ }, }, workos: { + provider: env('AUTH_WORKOS_PROVIDER', 'dashboard'), dashboard: { clientId: env('WORKOS_CLIENT_ID'), apiKey: env('WORKOS_API_KEY'), - cookiePassword: env('WORKOS_COOKIE_PASSWORD'), redirectUri: env('WORKOS_REDIRECT_URI'), - sessionCookie: env('WORKOS_SESSION_COOKIE', "wos-session"), }, }, // Add a dedicated guard and provider if WorkOS users should resolve through a different model. diff --git a/apps/blog-nuxt/app/app.vue b/apps/blog-nuxt/app/app.vue index 76c87491..3f5562c6 100644 --- a/apps/blog-nuxt/app/app.vue +++ b/apps/blog-nuxt/app/app.vue @@ -21,6 +21,9 @@ async function logout() { diff --git a/apps/blog-nuxt/config/auth.ts b/apps/blog-nuxt/config/auth.ts index 2c9f962b..b54d336e 100644 --- a/apps/blog-nuxt/config/auth.ts +++ b/apps/blog-nuxt/config/auth.ts @@ -41,7 +41,6 @@ export default defineAuthConfig({ personalAccessTokens: { defaultAbilities: [], }, - socialEncryptionKey: env('AUTH_SOCIAL_ENCRYPTION_KEY'), social: { google: { clientId: env('AUTH_GOOGLE_CLIENT_ID'), @@ -57,12 +56,11 @@ export default defineAuthConfig({ }, }, workos: { + provider: env('AUTH_WORKOS_PROVIDER', 'dashboard'), dashboard: { clientId: env('WORKOS_CLIENT_ID'), apiKey: env('WORKOS_API_KEY'), - cookiePassword: env('WORKOS_COOKIE_PASSWORD'), redirectUri: env('WORKOS_REDIRECT_URI'), - sessionCookie: env('WORKOS_SESSION_COOKIE', "wos-session"), }, }, // Add a dedicated guard and provider if WorkOS users should resolve through a different model. diff --git a/apps/blog-nuxt/server/api/auth/workos/callback.get.ts b/apps/blog-nuxt/server/api/auth/workos/callback.get.ts new file mode 100644 index 00000000..d3d54428 --- /dev/null +++ b/apps/blog-nuxt/server/api/auth/workos/callback.get.ts @@ -0,0 +1,11 @@ +import { completeWorkosAuth } from '@holo-js/auth-workos' +import { sendRedirect } from 'h3' + +export default defineEventHandler(async (event) => { + const result = await completeWorkosAuth(event) + if (!result.ok) { + return await sendRedirect(event, `/login?error=${encodeURIComponent(result.code)}`, 303) + } + + return await sendRedirect(event, '/admin', 303) +}) diff --git a/apps/blog-nuxt/server/api/auth/workos/login.get.ts b/apps/blog-nuxt/server/api/auth/workos/login.get.ts new file mode 100644 index 00000000..465b7be5 --- /dev/null +++ b/apps/blog-nuxt/server/api/auth/workos/login.get.ts @@ -0,0 +1,5 @@ +import { loginWithWorkos } from '@holo-js/auth-workos' + +export default defineEventHandler(async (event) => { + return await loginWithWorkos(event) +}) diff --git a/apps/blog-nuxt/server/api/auth/workos/logout.post.ts b/apps/blog-nuxt/server/api/auth/workos/logout.post.ts new file mode 100644 index 00000000..0dd8dac4 --- /dev/null +++ b/apps/blog-nuxt/server/api/auth/workos/logout.post.ts @@ -0,0 +1,14 @@ +import { logoutWithWorkos } from '@holo-js/auth-workos' +import { createError, sendRedirect } from 'h3' + +export default defineEventHandler(async (event) => { + const result = await logoutWithWorkos(event) + if (!result.ok) { + throw createError({ + statusCode: 422, + statusMessage: result.message, + }) + } + + return await sendRedirect(event, result.url, 303) +}) diff --git a/apps/blog-nuxt/server/api/auth/workos/register.get.ts b/apps/blog-nuxt/server/api/auth/workos/register.get.ts new file mode 100644 index 00000000..0beb0684 --- /dev/null +++ b/apps/blog-nuxt/server/api/auth/workos/register.get.ts @@ -0,0 +1,5 @@ +import { registerWithWorkos } from '@holo-js/auth-workos' + +export default defineEventHandler(async (event) => { + return await registerWithWorkos(event) +}) diff --git a/apps/blog-sveltekit/config/auth.ts b/apps/blog-sveltekit/config/auth.ts index 2c9f962b..b54d336e 100644 --- a/apps/blog-sveltekit/config/auth.ts +++ b/apps/blog-sveltekit/config/auth.ts @@ -41,7 +41,6 @@ export default defineAuthConfig({ personalAccessTokens: { defaultAbilities: [], }, - socialEncryptionKey: env('AUTH_SOCIAL_ENCRYPTION_KEY'), social: { google: { clientId: env('AUTH_GOOGLE_CLIENT_ID'), @@ -57,12 +56,11 @@ export default defineAuthConfig({ }, }, workos: { + provider: env('AUTH_WORKOS_PROVIDER', 'dashboard'), dashboard: { clientId: env('WORKOS_CLIENT_ID'), apiKey: env('WORKOS_API_KEY'), - cookiePassword: env('WORKOS_COOKIE_PASSWORD'), redirectUri: env('WORKOS_REDIRECT_URI'), - sessionCookie: env('WORKOS_SESSION_COOKIE', "wos-session"), }, }, // Add a dedicated guard and provider if WorkOS users should resolve through a different model. diff --git a/apps/blog-sveltekit/src/routes/+layout.svelte b/apps/blog-sveltekit/src/routes/+layout.svelte index dc1f257d..87a94ab3 100644 --- a/apps/blog-sveltekit/src/routes/+layout.svelte +++ b/apps/blog-sveltekit/src/routes/+layout.svelte @@ -42,6 +42,9 @@ {#if auth.authenticated} {displayName} + + +
{:else} Login Register @@ -94,6 +97,9 @@ opacity: 0.6; pointer-events: none; } + .logout-form { + display: inline; + } a { color: #cbd5e1; text-decoration: none; diff --git a/apps/blog-sveltekit/src/routes/api/auth/workos/callback/+server.ts b/apps/blog-sveltekit/src/routes/api/auth/workos/callback/+server.ts new file mode 100644 index 00000000..a95a47b1 --- /dev/null +++ b/apps/blog-sveltekit/src/routes/api/auth/workos/callback/+server.ts @@ -0,0 +1,11 @@ +import { redirect, type RequestHandler } from '@sveltejs/kit' +import { completeWorkosAuth } from '@holo-js/auth-workos' + +export const GET = (async (event) => { + const result = await completeWorkosAuth(event) + if (!result.ok) { + throw redirect(303, `/login?error=${encodeURIComponent(result.code)}`) + } + + throw redirect(303, '/admin') +}) satisfies RequestHandler diff --git a/apps/blog-sveltekit/src/routes/api/auth/workos/login/+server.ts b/apps/blog-sveltekit/src/routes/api/auth/workos/login/+server.ts new file mode 100644 index 00000000..72ccebec --- /dev/null +++ b/apps/blog-sveltekit/src/routes/api/auth/workos/login/+server.ts @@ -0,0 +1,6 @@ +import { loginWithWorkos } from '@holo-js/auth-workos' +import type { RequestHandler } from './$types' + +export const GET = (async (event) => { + return await loginWithWorkos(event) +}) satisfies RequestHandler diff --git a/apps/blog-sveltekit/src/routes/api/auth/workos/logout/+server.ts b/apps/blog-sveltekit/src/routes/api/auth/workos/logout/+server.ts new file mode 100644 index 00000000..f1e93aca --- /dev/null +++ b/apps/blog-sveltekit/src/routes/api/auth/workos/logout/+server.ts @@ -0,0 +1,11 @@ +import { redirect, type RequestHandler } from '@sveltejs/kit' +import { logoutWithWorkos } from '@holo-js/auth-workos' + +export const POST = (async (event) => { + const result = await logoutWithWorkos(event) + if (!result.ok) { + return Response.json(result, { status: 422 }) + } + + throw redirect(303, result.url) +}) satisfies RequestHandler diff --git a/apps/blog-sveltekit/src/routes/api/auth/workos/register/+server.ts b/apps/blog-sveltekit/src/routes/api/auth/workos/register/+server.ts new file mode 100644 index 00000000..e271c22f --- /dev/null +++ b/apps/blog-sveltekit/src/routes/api/auth/workos/register/+server.ts @@ -0,0 +1,6 @@ +import { registerWithWorkos } from '@holo-js/auth-workos' +import type { RequestHandler } from './$types' + +export const GET = (async (event) => { + return await registerWithWorkos(event) +}) satisfies RequestHandler diff --git a/apps/blog-sveltekit/src/routes/login/+page.svelte b/apps/blog-sveltekit/src/routes/login/+page.svelte index 8a3523e6..4a472483 100644 --- a/apps/blog-sveltekit/src/routes/login/+page.svelte +++ b/apps/blog-sveltekit/src/routes/login/+page.svelte @@ -30,6 +30,7 @@
{ event.preventDefault(); form.submit() }}> diff --git a/apps/blog-sveltekit/src/routes/register/+page.svelte b/apps/blog-sveltekit/src/routes/register/+page.svelte index a7094569..2701da8d 100644 --- a/apps/blog-sveltekit/src/routes/register/+page.svelte +++ b/apps/blog-sveltekit/src/routes/register/+page.svelte @@ -96,6 +96,7 @@ {/if} Already have an account? + Register with WorkOS