diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b7d9e31..9674fa9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,10 @@ "dependencies": { "@prisma/client": "^6.5.0", "@supabase/supabase-js": "^2.49.1", + "axios": "^1.8.4", "d3": "^7.9.0", + "fast-xml-parser": "^5.0.9", + "jose": "^6.0.10", "next": "15.1.6", "react": "^19.0.0", "react-dom": "^19.0.0" @@ -2075,6 +2078,11 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2099,6 +2107,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2169,7 +2187,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -2284,6 +2301,17 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -2810,6 +2838,14 @@ "robust-predicates": "^3.0.2" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -2835,7 +2871,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -2933,7 +2968,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2942,7 +2976,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2978,7 +3011,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -2990,7 +3022,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -3569,6 +3600,23 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.0.9.tgz", + "integrity": "sha512-2mBwCiuW3ycKQQ6SOesSB8WeF+fIGb6I/GG5vU5/XEptwFFhp9PE8b9O7fbs2dpq9fXn4ULR3UsfydNUCntf5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^2.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", @@ -3637,6 +3685,25 @@ "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", @@ -3652,6 +3719,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3670,7 +3751,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3708,7 +3788,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", @@ -3732,7 +3811,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -3814,7 +3892,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3886,7 +3963,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3898,7 +3974,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -3913,7 +3988,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -4386,6 +4460,14 @@ "node": ">= 0.4" } }, + "node_modules/jose": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz", + "integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4526,7 +4608,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -4553,6 +4634,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4971,6 +5071,11 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5559,6 +5664,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.0.5.tgz", + "integrity": "sha512-YAT3K/sgpCUxhxNMrrdhtod3jckkpYwH6JAuwmUdXZsmzH1wUyzTMrrK2wYCEEqlKwrWDd35NeuUkbBy/1iK+Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1887d5c..7be00a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,10 @@ "dependencies": { "@prisma/client": "^6.5.0", "@supabase/supabase-js": "^2.49.1", + "axios": "^1.8.4", "d3": "^7.9.0", + "fast-xml-parser": "^5.0.9", + "jose": "^6.0.10", "next": "15.1.6", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/frontend/src/app/api/auth/cas/callback/route.js b/frontend/src/app/api/auth/cas/callback/route.js new file mode 100644 index 0000000..ae6a053 --- /dev/null +++ b/frontend/src/app/api/auth/cas/callback/route.js @@ -0,0 +1,156 @@ + +// callback/route.js - using password auth after CAS +import { NextResponse } from 'next/server'; +import { createClient } from '@supabase/supabase-js'; +import axios from 'axios'; +import { XMLParser } from 'fast-xml-parser'; +import { cookies } from 'next/headers'; +import crypto from 'crypto'; + +export async function GET(request) +{ + const url = new URL(request.url); + const ticket = url.searchParams.get('ticket'); + + if (!ticket) { + return NextResponse.redirect(new URL('/login?error=No+ticket+provided', request.url)); + } + + try { + const serviceUrl = url.origin + '/api/auth/cas/callback'; + const validateUrl = `https://secure.its.yale.edu/cas/serviceValidate?ticket=${ticket}&service=${encodeURIComponent(serviceUrl)}`; + + const response = await axios.get(validateUrl); + + const parser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: '@_' + }); + const result = parser.parse(response.data); + + if (result['cas:serviceResponse'] && result['cas:serviceResponse']['cas:authenticationSuccess']) { + const authSuccess = result['cas:serviceResponse']['cas:authenticationSuccess']; + const netID = authSuccess['cas:user']; + + // Create Supabase client with service role key + const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL, + process.env.SUPABASE_SERVICE_ROLE_KEY + ); + + // Check if user exists in users table by net_id + const { data: existingUser, error: userError } = await supabase + .from('users') + .select('id, net_id') + .eq('net_id', netID) + .single(); + + if (userError && userError.code !== 'PGRST116') { + console.error('Error checking user:', userError); + return NextResponse.redirect(new URL('/login?error=Database+error', request.url)); + } + + // Generate a secure random password + const generatePassword = () => { + return crypto.randomBytes(16).toString('hex'); + }; + + let userId; + let password = generatePassword(); + + if (existingUser) { + // User exists, check if auth user exists with same ID + const { data: authData } = await supabase.auth.admin.getUserById(existingUser.id); + + if (authData?.user) { + // Auth user exists, update password + const { error: updateError } = await supabase.auth.admin.updateUserById( + existingUser.id, + { password } + ); + + if (updateError) { + console.error('Error updating user password:', updateError); + return NextResponse.redirect(new URL('/login?error=Password+update+failed', request.url)); + } + + userId = existingUser.id; + } else { + // Auth user doesn't exist, create new auth user with specified ID + const { data: newAuthUser, error: createAuthError } = await supabase.auth.admin.createUser({ + email: `${netID}@yale.edu`, + password, + email_confirm: true, + user_metadata: { netid: netID }, + app_metadata: { provider: 'cas' }, + user_id: existingUser.id + }); + + if (createAuthError) { + console.error('Error creating auth user:', createAuthError); + return NextResponse.redirect(new URL('/login?error=User+creation+failed', request.url)); + } + + userId = newAuthUser.user.id; + } + } else { + // User doesn't exist, create new auth user + const { data: newAuthUser, error: createAuthError } = await supabase.auth.admin.createUser({ + email: `${netID}@yale.edu`, + password, + email_confirm: true, + user_metadata: { netid: netID }, + app_metadata: { provider: 'cas' } + }); + + if (createAuthError) { + console.error('Error creating auth user:', createAuthError); + return NextResponse.redirect(new URL('/login?error=User+creation+failed', request.url)); + } + + userId = newAuthUser.user.id; + + // Create user record + const { error: insertError } = await supabase + .from('users') + .insert({ + id: userId, + net_id: netID + }); + + if (insertError) { + console.error('Error creating user record:', insertError); + return NextResponse.redirect(new URL('/login?error=User+record+creation+failed', request.url)); + } + } + + // Sign in with email/password + const { data: signInData, error: signInError } = await supabase.auth.signInWithPassword({ + email: `${netID}@yale.edu`, + password + }); + + if (signInError) { + console.error('Error signing in:', signInError); + return NextResponse.redirect(new URL('/login?error=Sign+in+failed', request.url)); + } + + // Set the auth cookie + const cookieStore = await cookies(); + cookieStore.set('sb-auth-token', 'true', { + path: '/', + secure: process.env.NODE_ENV === 'production', + maxAge: 60 * 60 * 24 * 7 // 1 week + }); + + // Redirect to destination + return NextResponse.redirect(new URL('/graduation', request.url)); + } else { + console.error('CAS authentication failed'); + return NextResponse.redirect(new URL('/login?error=CAS+authentication+failed', request.url)); + } + } catch (error) { + console.error('CAS validation error:', error); + return NextResponse.redirect(new URL('/login?error=Authentication+failed', request.url)); + } +} diff --git a/frontend/src/app/api/auth/cas/login/route.js b/frontend/src/app/api/auth/cas/login/route.js new file mode 100644 index 0000000..de8eb7f --- /dev/null +++ b/frontend/src/app/api/auth/cas/login/route.js @@ -0,0 +1,14 @@ + +// login/route.js +import { redirect } from 'next/navigation'; + +export async function GET(request) +{ + const casLoginUrl = 'https://secure.its.yale.edu/cas/login'; + + const url = new URL(request.url); + let serviceUrl = url.origin + '/api/auth/cas/callback'; + console.log('Service URL for CAS login:', serviceUrl); + + return redirect(`${casLoginUrl}?service=${encodeURIComponent(serviceUrl)}`); +} diff --git a/frontend/src/app/api/auth/logout/route.js b/frontend/src/app/api/auth/logout/route.js new file mode 100644 index 0000000..aab4c94 --- /dev/null +++ b/frontend/src/app/api/auth/logout/route.js @@ -0,0 +1,37 @@ + +import { NextResponse } from 'next/server'; +import { createClient } from '@supabase/supabase-js'; +import { cookies } from 'next/headers'; + +export async function GET(request) { + const cookieStore = cookies(); + const accessToken = cookieStore.get('sb-access-token')?.value; + const refreshToken = cookieStore.get('sb-refresh-token')?.value; + + if (accessToken && refreshToken) { + // Create Supabase client + const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL, + process.env.SUPABASE_SERVICE_ROLE_KEY, + { + auth: { + persistSession: false + } + } + ); + + // Invalidate the session + await supabase.auth.admin.signOut({ + scope: 'global' + }); + } + + // Create response with redirect to CAS logout + const response = NextResponse.redirect('https://secure.its.yale.edu/cas/logout'); + + // Clear the cookies + response.cookies.delete('sb-access-token'); + response.cookies.delete('sb-refresh-token'); + + return response; +} diff --git a/frontend/src/app/api/auth/session/route.js b/frontend/src/app/api/auth/session/route.js new file mode 100644 index 0000000..97ca15b --- /dev/null +++ b/frontend/src/app/api/auth/session/route.js @@ -0,0 +1,56 @@ + +import { NextResponse } from 'next/server'; +import { createClient } from '@supabase/supabase-js'; +import { cookies } from 'next/headers'; + +export async function GET() { + const cookieStore = cookies(); + const accessToken = cookieStore.get('sb-access-token')?.value; + const refreshToken = cookieStore.get('sb-refresh-token')?.value; + + if (!accessToken || !refreshToken) { + return NextResponse.json({ + loggedIn: false + }); + } + + // Create Supabase client + const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, + { + auth: { + persistSession: false, + autoRefreshToken: false + } + } + ); + + // Set session manually using the cookies + const { data: { session }, error } = await supabase.auth.setSession({ + access_token: accessToken, + refresh_token: refreshToken + }); + + if (error || !session) { + return NextResponse.json({ + loggedIn: false + }); + } + + // Get user data from users table + const { data: userData } = await supabase + .from('users') + .select('net_id, name') + .eq('id', session.user.id) + .single(); + + return NextResponse.json({ + loggedIn: true, + user: { + id: session.user.id, + netid: userData?.net_id || session.user.user_metadata?.netid, + name: userData?.name + } + }); +} diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index 81263e4..7d0cecc 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -1,40 +1,14 @@ "use client"; -import { useRouter } from "next/navigation"; import Style from "./Login.module.css"; - -import { useAuth } from "@/context/AuthProvider"; import NavBar from "@/components/navbar/NavBar"; function Login() { - const router = useRouter(); - const { setAuth, setUser } = useAuth(); - - const handleLogin = async () => { - try { - const response = await fetch("/api/login", { method: "GET" }); - - if (!response.ok) { - throw new Error("Login failed"); - } - - const data = await response.json(); - setAuth({ loggedIn: true }); - setUser(data); - - if(!data.onboard){ - router.push("/account"); - }else{ - router.push("/graduation"); - } - - } catch (error) { - console.error("❌ Login error:", error); - } + const handleLogin = () => { + window.location.href = "/api/auth/cas/login"; }; - return (