Merge dev into main

This commit is contained in:
mARTin 2026-03-03 14:05:49 +01:00
commit 3a4e04702a
7 changed files with 78 additions and 6 deletions

View File

@ -35,6 +35,8 @@ The application requires the following environment variables:
- `DATABASE_URL`: PostgreSQL connection string - `DATABASE_URL`: PostgreSQL connection string
- `NEXTAUTH_SECRET`: Secret for NextAuth.js - `NEXTAUTH_SECRET`: Secret for NextAuth.js
- `NEXT_PUBLIC_BASE_URL`: Public URL of the application - `NEXT_PUBLIC_BASE_URL`: Public URL of the application
- `MICROSOFT_CLIENT_ID`: Azure AD Application (client) ID for Microsoft Login
- `MICROSOFT_CLIENT_SECRET`: Azure AD Application secret for Microsoft Login
### Database Configuration ### Database Configuration

11
package-lock.json generated
View File

@ -10,6 +10,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@auth/prisma-adapter": "^2.11.1", "@auth/prisma-adapter": "^2.11.1",
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^5.22.0", "@prisma/client": "^5.22.0",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/nodemailer": "^7.0.11", "@types/nodemailer": "^7.0.11",
@ -1955,6 +1956,16 @@
"@tybys/wasm-util": "^0.10.0" "@tybys/wasm-util": "^0.10.0"
} }
}, },
"node_modules/@next-auth/prisma-adapter": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.7.tgz",
"integrity": "sha512-Cdko4KfcmKjsyHFrWwZ//lfLUbcLqlyFqjd/nYE2m3aZ7tjMNUjpks47iw7NTCnXf+5UWz5Ypyt1dSs1EP5QJw==",
"license": "ISC",
"peerDependencies": {
"@prisma/client": ">=2.26.0 || >=3",
"next-auth": "^4"
}
},
"node_modules/@next/env": { "node_modules/@next/env": {
"version": "14.2.35", "version": "14.2.35",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz",

View File

@ -22,6 +22,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@auth/prisma-adapter": "^2.11.1", "@auth/prisma-adapter": "^2.11.1",
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^5.22.0", "@prisma/client": "^5.22.0",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/nodemailer": "^7.0.11", "@types/nodemailer": "^7.0.11",

View File

@ -50,6 +50,10 @@ function LoginContent() {
</div> </div>
)} )}
<p className="weekly-auth-recommended">
Recommended: Log in with email
</p>
{/* Login Form */} {/* Login Form */}
<form onSubmit={handleCredentialsSignIn} className="weekly-auth-form"> <form onSubmit={handleCredentialsSignIn} className="weekly-auth-form">
<div className="weekly-auth-field"> <div className="weekly-auth-field">
@ -85,7 +89,7 @@ function LoginContent() {
{/* Divider */} {/* Divider */}
<div className="weekly-auth-divider"> <div className="weekly-auth-divider">
<span>or</span> <span>or trust US Big Tech</span>
</div> </div>
{/* Google Sign In */} {/* Google Sign In */}
@ -118,6 +122,20 @@ function LoginContent() {
Continue with Apple Continue with Apple
</button> </button>
{/* Microsoft Sign In */}
<button
type="button"
onClick={() => { setIsLoading(true); signIn('azure-ad', { callbackUrl }); }}
disabled={isLoading}
className="weekly-auth-button"
style={{ marginTop: '0.75rem', backgroundColor: '#0078D4', color: '#fff', border: '1px solid #0078D4' }}
>
<svg className="microsoft-icon" viewBox="0 0 21 21" width="18" height="18" fill="currentColor">
<path d="M10 0H0v10h10V0zM21 0H11v10h10V0zM10 11H0v10h10V11zM21 11H11v10h10V11z" />
</svg>
Continue with Microsoft
</button>
{/* Links */} {/* Links */}
<div className="weekly-auth-links"> <div className="weekly-auth-links">
<Link href="/auth/forgot-password" className="weekly-auth-link"> <Link href="/auth/forgot-password" className="weekly-auth-link">

View File

@ -91,6 +91,10 @@ export default function SignupPage() {
</div> </div>
)} )}
<p className="weekly-auth-recommended">
Recommended: Sign up with email
</p>
{/* Signup Form */} {/* Signup Form */}
<form onSubmit={handleSignup} className="weekly-auth-form"> <form onSubmit={handleSignup} className="weekly-auth-form">
<div className="weekly-auth-field"> <div className="weekly-auth-field">
@ -150,7 +154,7 @@ export default function SignupPage() {
{/* Divider */} {/* Divider */}
<div className="weekly-auth-divider"> <div className="weekly-auth-divider">
<span>or</span> <span>or trust US Big Tech</span>
</div> </div>
{/* Google Sign Up */} {/* Google Sign Up */}
@ -183,6 +187,20 @@ export default function SignupPage() {
Sign up with Apple Sign up with Apple
</button> </button>
{/* Microsoft Sign Up */}
<button
type="button"
onClick={() => { setIsLoading(true); signIn('azure-ad', { callbackUrl: '/tasks' }); }}
disabled={isLoading}
className="weekly-auth-button"
style={{ marginTop: '0.75rem', backgroundColor: '#0078D4', color: '#fff', border: '1px solid #0078D4' }}
>
<svg className="microsoft-icon" viewBox="0 0 21 21" width="18" height="18" fill="currentColor">
<path d="M10 0H0v10h10V0zM21 0H11v10h10V0zM10 11H0v10h10V11zM21 11H11v10h10V11z" />
</svg>
Sign up with Microsoft
</button>
{/* Links */} {/* Links */}
<div className="weekly-auth-links"> <div className="weekly-auth-links">
<span className="weekly-auth-text">Already have an account?</span> <span className="weekly-auth-text">Already have an account?</span>

View File

@ -186,6 +186,14 @@ h3 {
color: #64748b; color: #64748b;
} }
.weekly-auth-recommended {
font-size: 0.85rem;
color: #475569;
text-align: center;
margin-bottom: 1rem;
font-weight: 500;
}
.time-indicator { .time-indicator {
font-size: 0.875rem; font-size: 0.875rem;
color: #3b82f6; color: #3b82f6;
@ -330,7 +338,6 @@ h3 {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
} }
/* Auth Form */
.auth-form { .auth-form {
background: white; background: white;
border-radius: 8px; border-radius: 8px;

View File

@ -1,13 +1,14 @@
import { NextAuthOptions } from "next-auth"; import { NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google"; import GoogleProvider from "next-auth/providers/google";
import AzureADProvider from "next-auth/providers/azure-ad";
import AppleProvider from "next-auth/providers/apple"; import AppleProvider from "next-auth/providers/apple";
import { PrismaAdapter } from "@auth/prisma-adapter"; import { PrismaAdapter } from "@next-auth/prisma-adapter";
import CredentialsProvider from "next-auth/providers/credentials"; import CredentialsProvider from "next-auth/providers/credentials";
import { compare } from "bcryptjs"; import { compare } from "bcryptjs";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
export const authOptions: NextAuthOptions = { export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma) as any, adapter: PrismaAdapter(prisma),
providers: [ providers: [
GoogleProvider({ GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID || "", clientId: process.env.GOOGLE_CLIENT_ID || "",
@ -17,7 +18,8 @@ export const authOptions: NextAuthOptions = {
params: { params: {
prompt: "consent", prompt: "consent",
access_type: "offline", access_type: "offline",
response_type: "code" response_type: "code",
scope: "openid profile email https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/tasks"
} }
} }
}), }),
@ -26,6 +28,18 @@ export const authOptions: NextAuthOptions = {
clientSecret: process.env.APPLE_SECRET || "", clientSecret: process.env.APPLE_SECRET || "",
allowDangerousEmailAccountLinking: true, allowDangerousEmailAccountLinking: true,
}), }),
AzureADProvider({
clientId: process.env.MICROSOFT_CLIENT_ID || "",
clientSecret: process.env.MICROSOFT_CLIENT_SECRET || "",
tenantId: "common",
allowDangerousEmailAccountLinking: true,
authorization: {
params: {
prompt: "consent",
scope: "openid profile email offline_access user.read Calendars.ReadWrite Tasks.ReadWrite"
}
},
}),
CredentialsProvider({ CredentialsProvider({
name: "credentials", name: "credentials",
credentials: { credentials: {
@ -70,6 +84,7 @@ export const authOptions: NextAuthOptions = {
} }
}) })
], ],
debug: true,
session: { session: {
strategy: "jwt" strategy: "jwt"
}, },