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,