Authentication in both component and middleware

After did some search I'm still confused how solid start handles the authentication. In some auth libraries, they are using middleware to protect api routes and actions but not components(middlewares do not get called when route changes). I looked at the auth example, it only applies a simple action, not a layout. I simply created an auth middleware (from lucia example) and protected component, but I think the solution is not clear.
4 Replies
colinshen
colinshenOP8mo ago
middleware
export default async function authMiddleware(event: FetchEvent) {
const sessionId =
getCookie(event.nativeEvent, lucia.sessionCookieName) ?? null;
if (!sessionId) {
event.locals.user = null;
event.locals.session = null;
return;
}
// null when not found in database, expired
// fresh = true when half of expiration
const { session, user } = await lucia.validateSession(sessionId);
if (session && session.fresh) {
const sessionCookie = lucia.createSessionCookie(session.id);
setCookie(
event.nativeEvent,
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes,
);
}
if (!session) {
const sessionCookie = lucia.createBlankSessionCookie();
setCookie(
event.nativeEvent,
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes,
);
}
event.locals.user = user;
event.locals.session = session;
}
export default async function authMiddleware(event: FetchEvent) {
const sessionId =
getCookie(event.nativeEvent, lucia.sessionCookieName) ?? null;
if (!sessionId) {
event.locals.user = null;
event.locals.session = null;
return;
}
// null when not found in database, expired
// fresh = true when half of expiration
const { session, user } = await lucia.validateSession(sessionId);
if (session && session.fresh) {
const sessionCookie = lucia.createSessionCookie(session.id);
setCookie(
event.nativeEvent,
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes,
);
}
if (!session) {
const sessionCookie = lucia.createBlankSessionCookie();
setCookie(
event.nativeEvent,
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes,
);
}
event.locals.user = user;
event.locals.session = session;
}
protected component
const getUser = cache(async () => {
"use server";
const event = getRequestEvent();
const user = event?.locals.user;
if (user) {
return user;
} else {
// return null
throw redirect("/login");
}
}, "get-user");
export const ProtectedRoute = (props: ParentProps) => {
const user = createAsync(() => getUser());
if (user()) {
return props.children;
} else {
return <Navigate href="/login" />;
}
};
const getUser = cache(async () => {
"use server";
const event = getRequestEvent();
const user = event?.locals.user;
if (user) {
return user;
} else {
// return null
throw redirect("/login");
}
}, "get-user");
export const ProtectedRoute = (props: ParentProps) => {
const user = createAsync(() => getUser());
if (user()) {
return props.children;
} else {
return <Navigate href="/login" />;
}
};
Problems: 1. If session or user is null, middleware should return a redirect to login page, but redirect function or sendRedirect(from vinxi) cause too many redirect error. If the middleware logic is correct, how to redirect in middleware? 2. In protected component, I can easily redirect in server functions. Doing it again in component with Navigate is definitely not necessary. Should I just return null in the function?
peerreynders
peerreynders8mo ago
@solidjs/start/server re-exports h3's sendRedirect (which vinxi wraps) which you can use in middleware. Example I don't see how sendRedirect is used in your code sample.
if (!sessionId) {
event.locals.user = null;
event.locals.session = null;
// this is where I would expect…
return sendRedirect(event, loginHref);
}
if (!sessionId) {
event.locals.user = null;
event.locals.session = null;
// this is where I would expect…
return sendRedirect(event, loginHref);
}
That said you have to let the request for loginHref (and any requests supporting that page) pass otherwise you will get into an infinite loop of redirects.
Response - h3
Utilities to send response headers and data
GitHub
solid-start-demo-login/src/middleware.ts at restart · peerreynders/...
SolidStart seed project with simple user management for demonstration projects. - peerreynders/solid-start-demo-login
colinshen
colinshenOP8mo ago
Thanks! I need to pass the login to make it work. Do you think middleware is enough for authentication, not using an auth component or context?
peerreynders
peerreynders8mo ago
Your component code simply verifies that the middleware already performed the required authentication. So it really doesn't "authenticate" anything, it just ensures that it hasn't been reached via an unprotected route (i.e. one that was inadvertently left unprotected by the middleware); and it's still vulnerable if for example the authentication cookie survived for longer than it should. So if there are "sensitive areas" OWASP recommends to force re-authentication even if the user is already logged in.
Authentication - OWASP Cheat Sheet Series
Website with the collection of all the cheat sheets of the project.
Want results from more Discord servers?
Add your server