-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix up login form to display errors better
- Loading branch information
1 parent
01f4e40
commit 9c562c8
Showing
3 changed files
with
186 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
166 changes: 120 additions & 46 deletions
166
services/barn-ui/src/routes/(guest)/login/+page.server.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,136 @@ | ||
import { redirect } from '@sveltejs/kit'; | ||
import { redirect, error } from '@sveltejs/kit'; | ||
import type { Actions, PageServerLoad } from './$types'; | ||
import { env } from '$env/dynamic/private'; | ||
|
||
export const load: PageServerLoad = async ({ url, cookies, locals }) => { | ||
const token = url.searchParams.get('token'); | ||
if (token) { | ||
cookies.set('jwt', token, { | ||
path: '/', | ||
expires: new Date(Date.now() + 1000 * 60 * 60 * 24), | ||
sameSite: true | ||
}); | ||
} | ||
try { | ||
const token = url.searchParams.get('token'); | ||
if (token) { | ||
cookies.set('jwt', token, { | ||
path: '/', | ||
expires: new Date(Date.now() + 1000 * 60 * 60 * 24), | ||
sameSite: true, | ||
httpOnly: true, // Added security measure | ||
secure: process.env.NODE_ENV === 'production' // Secure in production | ||
}); | ||
} | ||
|
||
return { | ||
loggedIn: Boolean(locals.user || token) | ||
}; | ||
return { | ||
loggedIn: Boolean(locals.user || token) | ||
}; | ||
} catch (err) { | ||
console.error('Login load error:', err); | ||
throw error(500, 'Failed to process login request'); | ||
} | ||
}; | ||
|
||
export const actions = { | ||
default: async ({ request, cookies }) => { | ||
const data = await request.formData(); | ||
const username = data.get('username'); | ||
const password = data.get('password'); | ||
try { | ||
const data = await request.formData(); | ||
const username = data.get('username'); | ||
const password = data.get('password'); | ||
|
||
// Basic validation | ||
if (!username || !password) { | ||
return { | ||
error: 'Username and password are required', | ||
username: username?.toString() | ||
}; | ||
} | ||
// Validation | ||
if (!username || !password) { | ||
return { | ||
type: 'error', | ||
error: { | ||
message: 'Username and password are required' | ||
}, | ||
data: { | ||
username: username?.toString() | ||
} | ||
}; | ||
} | ||
|
||
// Input sanitization | ||
const sanitizedUsername = username.toString().trim(); | ||
const sanitizedPassword = password.toString(); | ||
|
||
if (!sanitizedUsername || !sanitizedPassword) { | ||
return { | ||
type: 'error', | ||
error: { | ||
message: 'Invalid input provided' | ||
}, | ||
data: { | ||
username: sanitizedUsername | ||
} | ||
}; | ||
} | ||
|
||
// API call | ||
let response; | ||
try { | ||
response = await fetch(`${env.API_URL}/auth/login`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ | ||
username: sanitizedUsername, | ||
password: sanitizedPassword | ||
}) | ||
}); | ||
} catch (fetchError) { | ||
console.error('Login API call failed:', fetchError); | ||
return { | ||
type: 'error', | ||
error: { | ||
message: 'Unable to connect to authentication service' | ||
}, | ||
data: { | ||
username: sanitizedUsername | ||
} | ||
}; | ||
} | ||
|
||
if (!response.ok) { | ||
const errorData = await response.json(); | ||
return { | ||
type: 'error', | ||
error: { | ||
message: errorData.message || 'Invalid credentials' | ||
}, | ||
data: { | ||
username: sanitizedUsername | ||
} | ||
}; | ||
} | ||
|
||
const { token } = await response.json(); | ||
|
||
if (!token) { | ||
return { | ||
type: 'error', | ||
error: { | ||
message: 'Invalid response from authentication service' | ||
}, | ||
data: { | ||
username: sanitizedUsername | ||
} | ||
}; | ||
} | ||
|
||
// Set cookie with security options | ||
cookies.set('jwt', token, { | ||
path: '/', | ||
expires: new Date(Date.now() + 1000 * 60 * 60 * 24), // 24 hours | ||
sameSite: true, | ||
httpOnly: true, // Prevents JavaScript access to the cookie | ||
secure: process.env.NODE_ENV === 'production' // Secure in production | ||
}); | ||
|
||
const response = await fetch(`${env.API_URL}/auth/login`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ | ||
username: username.toString(), | ||
password: password.toString() | ||
}) | ||
}); | ||
|
||
if (!response.ok) { | ||
const errorData = await response.json(); | ||
throw redirect(303, '/'); | ||
} catch (err) { | ||
console.error('Login action error:', err); | ||
return { | ||
error: errorData.message || 'Invalid credentials', | ||
username: username.toString() | ||
type: 'error', | ||
error: { | ||
message: 'An unexpected error occurred during login' | ||
} | ||
}; | ||
} | ||
const { token } = await response.json(); | ||
// Set the cookie so we can get the user again later | ||
cookies.set('jwt', token, { | ||
path: '/', | ||
expires: new Date(Date.now() + 1000 * 60 * 60 * 24), // 24 hours | ||
sameSite: true | ||
}); | ||
// Redirect the user | ||
throw redirect(303, '/'); | ||
} | ||
} satisfies Actions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters