From fd2999e9bb7e5ba98e05c751077aab8b050871f6 Mon Sep 17 00:00:00 2001 From: gibbyb Date: Fri, 19 Sep 2025 16:13:38 -0500 Subject: [PATCH] I can't believe it but I have sign in with Microsoft working! --- apps/next/src/app/(auth)/signin/page.tsx | 44 +++---------------- .../layout/auth/buttons/gibs-auth.tsx | 41 +++++++++++++++++ .../components/layout/auth/buttons/index.tsx | 2 + .../layout/auth/buttons/microsoft.tsx | 41 +++++++++++++++++ packages/backend/convex/_generated/api.d.ts | 10 ++++- packages/backend/convex/auth.config.ts | 3 -- packages/backend/convex/auth.ts | 30 +++++++------ packages/backend/convex/custom/auth/index.ts | 3 ++ .../convex/custom/auth/password/validate.ts | 12 +++++ .../convex/custom/auth/providers/entra.ts | 29 ++++++++++++ .../auth/providers/password.ts} | 8 ++-- 11 files changed, 162 insertions(+), 61 deletions(-) create mode 100644 apps/next/src/components/layout/auth/buttons/gibs-auth.tsx create mode 100644 apps/next/src/components/layout/auth/buttons/index.tsx create mode 100644 apps/next/src/components/layout/auth/buttons/microsoft.tsx create mode 100644 packages/backend/convex/custom/auth/index.ts create mode 100644 packages/backend/convex/custom/auth/password/validate.ts create mode 100644 packages/backend/convex/custom/auth/providers/entra.ts rename packages/backend/convex/{CustomPassword.ts => custom/auth/providers/password.ts} (56%) diff --git a/apps/next/src/app/(auth)/signin/page.tsx b/apps/next/src/app/(auth)/signin/page.tsx index 633bc5f..2af1367 100644 --- a/apps/next/src/app/(auth)/signin/page.tsx +++ b/apps/next/src/app/(auth)/signin/page.tsx @@ -3,13 +3,11 @@ import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import Link from 'next/link'; -import Image from 'next/image'; import { useAuthActions } from '@convex-dev/auth/react'; import { useRouter } from 'next/navigation'; import { ConvexError } from 'convex/values'; import { useState } from 'react'; import { - Button, Card, CardContent, Form, @@ -27,6 +25,10 @@ import { TabsTrigger, } from '@/components/ui'; import { toast } from 'sonner'; +import { + GibsAuthSignInButton, + MicrosoftSignInButton +} from '@/components/layout/auth/buttons'; import { PASSWORD_MIN, PASSWORD_MAX, PASSWORD_REGEX } from '@/lib/types'; const signInFormSchema = z.object({ @@ -228,24 +230,7 @@ const SignIn = () => {
- +
@@ -353,24 +338,7 @@ const SignIn = () => {
- +
diff --git a/apps/next/src/components/layout/auth/buttons/gibs-auth.tsx b/apps/next/src/components/layout/auth/buttons/gibs-auth.tsx new file mode 100644 index 0000000..8d3c2ec --- /dev/null +++ b/apps/next/src/components/layout/auth/buttons/gibs-auth.tsx @@ -0,0 +1,41 @@ +import Image from 'next/image'; +import { useAuthActions } from '@convex-dev/auth/react'; +import { Button, type buttonVariants } from '@/components/ui'; +import { type ComponentProps } from 'react'; +import { type VariantProps } from 'class-variance-authority'; + +type Props = { + buttonProps?: Omit, 'onClick'> & + VariantProps & { + asChild?: boolean; + }, + type?: 'signIn' | 'signUp'; +}; + +export const GibsAuthSignInButton = ({ + buttonProps, + type = 'signIn', +}: Props) => { + const { signIn } = useAuthActions(); + return ( + + ); +}; diff --git a/apps/next/src/components/layout/auth/buttons/index.tsx b/apps/next/src/components/layout/auth/buttons/index.tsx new file mode 100644 index 0000000..598a674 --- /dev/null +++ b/apps/next/src/components/layout/auth/buttons/index.tsx @@ -0,0 +1,2 @@ +export { GibsAuthSignInButton } from './gibs-auth'; +export { MicrosoftSignInButton } from './microsoft'; diff --git a/apps/next/src/components/layout/auth/buttons/microsoft.tsx b/apps/next/src/components/layout/auth/buttons/microsoft.tsx new file mode 100644 index 0000000..aaec1e9 --- /dev/null +++ b/apps/next/src/components/layout/auth/buttons/microsoft.tsx @@ -0,0 +1,41 @@ +import { useAuthActions } from '@convex-dev/auth/react'; +import { Button, type buttonVariants } from '@/components/ui'; +import { type ComponentProps } from 'react'; +import { type VariantProps } from 'class-variance-authority'; + +type Props = { + buttonProps?: Omit, 'onClick'> & + VariantProps & { + asChild?: boolean; + }, + type?: 'signIn' | 'signUp'; +}; + +export const MicrosoftSignInButton = ({ + buttonProps, + type = 'signIn', +}: Props) => { + const { signIn } = useAuthActions(); + return ( + + ); +}; diff --git a/packages/backend/convex/_generated/api.d.ts b/packages/backend/convex/_generated/api.d.ts index c076b6b..2567445 100644 --- a/packages/backend/convex/_generated/api.d.ts +++ b/packages/backend/convex/_generated/api.d.ts @@ -13,9 +13,12 @@ import type { FilterApi, FunctionReference, } from "convex/server"; -import type * as CustomPassword from "../CustomPassword.js"; import type * as auth from "../auth.js"; import type * as crons from "../crons.js"; +import type * as custom_auth_index from "../custom/auth/index.js"; +import type * as custom_auth_password_validate from "../custom/auth/password/validate.js"; +import type * as custom_auth_providers_entra from "../custom/auth/providers/entra.js"; +import type * as custom_auth_providers_password from "../custom/auth/providers/password.js"; import type * as files from "../files.js"; import type * as http from "../http.js"; import type * as statuses from "../statuses.js"; @@ -29,9 +32,12 @@ import type * as statuses from "../statuses.js"; * ``` */ declare const fullApi: ApiFromModules<{ - CustomPassword: typeof CustomPassword; auth: typeof auth; crons: typeof crons; + "custom/auth/index": typeof custom_auth_index; + "custom/auth/password/validate": typeof custom_auth_password_validate; + "custom/auth/providers/entra": typeof custom_auth_providers_entra; + "custom/auth/providers/password": typeof custom_auth_providers_password; files: typeof files; http: typeof http; statuses: typeof statuses; diff --git a/packages/backend/convex/auth.config.ts b/packages/backend/convex/auth.config.ts index 9d15cf1..40b63c7 100644 --- a/packages/backend/convex/auth.config.ts +++ b/packages/backend/convex/auth.config.ts @@ -1,6 +1,3 @@ -import Authentik from '@auth/core/providers/authentik'; -import MicrosoftEntraID from '@auth/core/providers/microsoft-entra-id'; - export default { providers: [ { diff --git a/packages/backend/convex/auth.ts b/packages/backend/convex/auth.ts index 530e909..662f2f4 100644 --- a/packages/backend/convex/auth.ts +++ b/packages/backend/convex/auth.ts @@ -8,13 +8,14 @@ import { import { api } from './_generated/api'; import { type Id } from './_generated/dataModel'; import { action, mutation, query } from './_generated/server'; -import Password from './CustomPassword'; import Authentik from '@auth/core/providers/authentik'; +import { Entra, Password, validatePassword, } from './custom/auth'; export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({ providers: [ Password, Authentik, + Entra, ], }); @@ -42,6 +43,20 @@ export const getUser = query(async (ctx) => { }; }); +// Add this temporarily to packages/backend/convex/auth.ts +export const debugMicrosoftConfig = action({ + args: {}, + handler: async (ctx, args) => { + console.log('Microsoft Entra ID Config Debug:', { + issuer: process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER, + clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID, + hasSecret: !!process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET, + secretLength: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET?.length, + }); + return { logged: true }; + }, +}); + export const getAllUsers = query(async (ctx) => { const users = await ctx.db.query('users').collect(); return users.map((u) => ({ @@ -133,19 +148,6 @@ export const updateUserAutomaticLunch = mutation({ }, }); -export const validatePassword = (password: string): boolean => { - if ( - password.length < 8 || - password.length > 100 || - !/\d/.test(password) || - !/[a-z]/.test(password) || - !/[A-Z]/.test(password) - ) { - return false; - } - return true; -}; - export const updateUserPassword = action({ args: { currentPassword: v.string(), diff --git a/packages/backend/convex/custom/auth/index.ts b/packages/backend/convex/custom/auth/index.ts new file mode 100644 index 0000000..8211fd5 --- /dev/null +++ b/packages/backend/convex/custom/auth/index.ts @@ -0,0 +1,3 @@ +export { validatePassword } from './password/validate'; +export { Entra } from './providers/entra'; +export { Password } from './providers/password'; diff --git a/packages/backend/convex/custom/auth/password/validate.ts b/packages/backend/convex/custom/auth/password/validate.ts new file mode 100644 index 0000000..60fe84c --- /dev/null +++ b/packages/backend/convex/custom/auth/password/validate.ts @@ -0,0 +1,12 @@ +export const validatePassword = (password: string): boolean => { + if ( + password.length < 8 || + password.length > 100 || + !/\d/.test(password) || + !/[a-z]/.test(password) || + !/[A-Z]/.test(password) + ) { + return false; + } + return true; +}; diff --git a/packages/backend/convex/custom/auth/providers/entra.ts b/packages/backend/convex/custom/auth/providers/entra.ts new file mode 100644 index 0000000..26b1143 --- /dev/null +++ b/packages/backend/convex/custom/auth/providers/entra.ts @@ -0,0 +1,29 @@ +import { type AuthProviderMaterializedConfig } from '@convex-dev/auth/server'; + +export const Entra: AuthProviderMaterializedConfig = { + id: 'microsoft-entra-id', + name: 'Microsoft Entra ID', + type: 'oauth', + issuer: process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER!, + client: { + id: process.env.AUTH_MICROSOFT_ENTRA_ID_ID!, + secret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET!, + }, + authorization: { + url: process.env.AUTH_MICROSOFT_ENTRA_ID_AUTH_URL!, + params: { + scope: 'openid profile email offline_access', + response_type: 'code', + }, + }, + token: + 'https://login.microsoftonline.com/16200986-86f1-44d2-974c-cfa99352722c/oauth2/v2.0/token', + userinfo: 'https://graph.microsoft.com/oidc/userinfo', + profile(profile) { + return { + id: profile.sub, + name: profile.name, + email: profile.email, + }; + }, +}; diff --git a/packages/backend/convex/CustomPassword.ts b/packages/backend/convex/custom/auth/providers/password.ts similarity index 56% rename from packages/backend/convex/CustomPassword.ts rename to packages/backend/convex/custom/auth/providers/password.ts index f4d06cf..64fec8f 100644 --- a/packages/backend/convex/CustomPassword.ts +++ b/packages/backend/convex/custom/auth/providers/password.ts @@ -1,9 +1,9 @@ import { ConvexError } from 'convex/values'; -import { Password } from '@convex-dev/auth/providers/Password'; -import { validatePassword } from './auth'; -import type { DataModel } from './_generated/dataModel'; +import { Password as DefaultPassword } from '@convex-dev/auth/providers/Password'; +import { validatePassword } from '../password/validate'; +import type { DataModel } from '../../../_generated/dataModel'; -export default Password({ +export const Password = DefaultPassword({ profile(params, ctx) { return { email: params.email as string,