Let's have an honest discussion about Lucia #1231
Replies: 30 comments 60 replies
-
I, of course personally, want keys they provide a sense of uniformity. As for passwords, I am wholeheartedly in the camp of discouraging their use. If there exists a pathway to implementing them, hand me the pieces I need to the foot gun and let me build it. Offer me resources, considerations, and disclaimers, but anything beyond that? Take care of it for me completely. Which of course, would contradict the discouragement of using them in the first place. |
Beta Was this translation helpful? Give feedback.
-
Rip the Band-Aid clean off. It only stings for a little bit. |
Beta Was this translation helpful? Give feedback.
-
I support removing keys. I'm already basically ignoring them by copying the user's Twitch ID into user attributes, just to avoid having to do a join when selecting a user by Twitch ID. I also don't use passwords but I think your reasoning is sound. |
Beta Was this translation helpful? Give feedback.
-
I feel like one of the best features of keys is being ignored here. I agree passwords should probably just die, but having unique keys for each login type allows an easy way to have users link multiple OAuth providers to the same account.
Keys also allow for easy future proofing for when there could be a mix of OAuth providers and Passkeys. |
Beta Was this translation helpful? Give feedback.
-
Speaking as a heavy user of Lucia ever since I discovered it sometime after the v1 release: I don't think Lucia, as a project, needs to go any further in terms of what it's trying to achieve.
This is on point. Auth is tedious and time consuming, and Lucia already takes a massive chunk of work out of my hands. But I think the most compelling aspect of Lucia is that the way it's structured also gives me enough flexibility to bend parts of it to meet my requirements. A few examples:
I think not covering request throttling, email verifications, and any other tasks not directly related to the session management aspect, was the correct decision so far: Lucia does one thing, and it tries to do it as well as it possibly can. I already have ownership of both the database and the API layer. If I want to use mailing, or implement request throttling, this should not fall into Lucia's umbrella of responsibilities.
This is fair, passwords are dangerous when handled incorrectly. Period. That being said though, one of the things that instantly drove me away from Auth.js is how they actively prevent you from configuring credential based authentication under some conditions. I get it, we want to make the transition to a passwordless future, and we want better tools to help us achieve that. But not every project is a greenfield one, and in the real world, we have business requirements to meet... Many times, these business requirements will tell us that a credential based authentication system is needed. So we will build/maintain them. So I agree, usage of credential based methods should be discouraged where possible (with visible warnings in the docs and some bits of developer education). But not removed.
I don't have a strong opinion about it to be completely honest. I think the feeling I have for them is, that they're fine. Yes, they are a Lucia specific concept, and yes, they do require you to adhere to a Lucia specific shape of database schema, but they never got in my way, even when working in making my authentication layer backward compatible with previous iterations (most databases will allow me to have a trigger to copy values from the I will say this though. Adding tables to a database is easier them removing them. On a personal level, for me, the removal of keys would incur in some serious refactoring on several projects where I want to keep using the latest version of Lucia. If you think this is a necessary change to realize your vision for the library, I'd say go for it. But be mindful that other authors of projects that currently use Lucia for their authentication layer in productions environments might not be too happy about it, so planning it throughly (migration guides on how to handle the database changes without losing data) is a good idea. |
Beta Was this translation helpful? Give feedback.
-
I agree with this so far (fairly new to Lucia). I appreciate Lucia's enthusiastic maintenance, docs, and thought given (e.g. architecture to sit between DB and framework, ability to use separate DBs for user data and sessions, etc). My perspective: As a user:
I'm interested in:
No opinion on keys. I'm too new to Lucia and reserving judgement. |
Beta Was this translation helpful? Give feedback.
-
I specifically moved away from Auth.js because of the limiting aspect of passwords. I understand that passwords are discouraged because due diligence still needs to be done. However, we still have requirements to have credentials logins. I don't understand the big push to get away from them when almost every new application from the private sector still requires them. I'm indifferent either way about the interactions that Lucia has with my user information. If you want to handle session management and provide oauth/password utility functions/providers, I'm game. If you go that route, I would at least lay out a checklist of things the user should provide to accomplish it correctly, not by giving code examples but by listing them out. I think for password authentication, it would be better if we had the chance to provide the users of the library additional utility functions for generating/validating TOTP to make credentials usage on the "safer" side. Removing the key table would be fine, as we can use the utility functions to validate the data from how we are storing it. Request throttling, email verification, and others should be kept out of auth as they are not directly auth-related, but maybe suggest the user implement them. Even then, you could still utilize Lucia in a passwordless capability. The user of the library would have to handle the passwordless part of it like a short-lived token at an endpoint that is emailed to them that would create a session. Medium.com does this with just an email address to create an account, and then they email you a link anytime you want to log in. This then makes a session and allows you to enter as you do. |
Beta Was this translation helpful? Give feedback.
-
Can you be specific about the care required? I only use Lucia for easier and secure way to handle sessions. That's all. For password reset and email verification, I think I can do it on my own. |
Beta Was this translation helpful? Give feedback.
-
I have followed this project on and off for awhile. Finally in the past 2 months implementing it into a production application. I have been able to make everything work well. I believe that a project should pick it one thing to do well and focus on that. So, I applaud you for really trying to find that one thing. I am too little experience to provide feedback on overall design. Though, I will say that I have recently been thinking that maybe it would be just easier to copy out the parts of Lucia that I like and integrated them directly into my project. As you mentioned, Auth is very closely tied to how a specific project works and it DB schema. Frankly I didn't really follow your above examples either... Overall, IMO I think that if you want to change the design, then you should just rip the bandaid. |
Beta Was this translation helpful? Give feedback.
-
Creator of Auth.js here.
I want to be clear. We do not make it harder to get credentials login working in Auth.js. We are pretty much on pair with the outlined goal here. Most people will be discouraged when they see that Auth.js has the artificial "limitation" that it doesn't work with a database. It literally takes ~5 lines to do it anyway though. If you don't realize it, you most likely should not implement it yourself. (We tried to make it more clear in the docs, see nextauthjs/next-auth#8482. I'll make sure this is reflected everywhere) As @pilcrowOnPaper says, you need to be willing to put the care required to pull it off. I understand that this is a common option still, and that's exactly why we are keeping the option to do it, and always will! Right now we have the bare minimum. That said, passwordless is a great alternative IMO, but I'm also optimistic about newer alternatives like WebAuthn/Passkeys, which we are looking into supporting out-of-the-box. That's all I wanted to say. Great post, great lib, respect @pilcrowOnPaper! 🎩 |
Beta Was this translation helpful? Give feedback.
-
My 2 cents as someone who has followed the development of Lucia but has never used it in a project I don't get why libraries are discouraging password authentication, it is by far the most popular method of authentication and the way to move away is not to persuade developers but rather users, I think every auth library should fully support it and the fact that it hard to pull of is all the more reason for libraries to support it so we don't have to worry about security |
Beta Was this translation helpful? Give feedback.
-
Maybe a little bit off-topic, but I'd love to see Lucia support WebAuthn/Passkeys. Is this possible with Lucia's keys? |
Beta Was this translation helpful? Give feedback.
-
As for the migration path, you have a few options:
Option 2 and 3 can be done with a few lines of SQL, but option 4 maybe a bit annoying (especially for SQLite) |
Beta Was this translation helpful? Give feedback.
-
I am mostly working on B2B & internal projects, IMHO, like it or not, password is still dominated in auth methods in these kinds of projects. Passwords are still having many upsides:
When using OAth, we are still using password but shift it complexity to OAuth provider with the price of freedom & privacy. Instead of discourage developer from password, maybe we can split passwords to it own package with their crucial modern features like password rules/strength, forgot/reset password, auto expiration, leak detection, suspicious behavior, ... It's a ton of work, but it still worth it. |
Beta Was this translation helpful? Give feedback.
-
Maybe this along with a cli would make it next level. |
Beta Was this translation helpful? Give feedback.
-
Here's some code examples for what this could mean: OAuthBasicconst tokens = await githubAuth.validateCallback(code);
const githubUser = await githubAuth.getGithubUser(tokens.accessToken);
const existingUser = await db
.table("user")
.where("github_user_id", "=", githubUser.id)
.get();
if (existingUser) {
const session = await auth.createSession(existingUser.id);
locals.auth.setSession(session);
}
const userId = auth.generateUserId();
await db.table("user").insert({
id: generateUserId(),
username: githubUser.login,
github_user_id: githubUser.login,
});
const session = await auth.createSession(userId);
locals.auth.setSession(session); Multiple providersconst github = new Github(clientId, clientSecret);
const tokens = await github.validateCallback(code);
const githubUser = await github.getGithubUser(tokens.accessToken);
const existingAccount = await db
.table("account")
.where("user_id", "=", githubUser.id)
.get();
if (existingAccount) {
const session = await auth.createSession(existingAccount.user_id);
locals.auth.setSession(session);
}
const userId = auth.generateUserId();
await db.transaction(
db.table("user").insert({
id: userId,
username: githubUser.login,
}),
db.table("oauth_account").insert({
provider: "github",
provider_user_id: githubUser.id,
user_id: userId,
})
);
const session = await auth.createSession(userId);
locals.auth.setSession(session); PasswordBasicimport { Argon2id } from "oslo/password";
const argon2id = new Argon2id();
const hashedPassword = await argon2id.hash(password);
const userId = auth.generateUserId();
await db.table("user").insert({
id: userId,
username,
hashed_password: hashedPassword,
}); const user = await db.table("user").where("username", "=", username).get();
if (!user) {
throw new Error("Invalid username or password");
}
const validPassword = await argon2id.verify(user.hashed_password, password);
if (!validPassword) {
throw new Error("Invalid username or password");
} Or, if you're storing the credentials in a different table: const credentials = await db
.table("credentials")
.innerJoin("user", "user.id", "credentials.user_id")
.where("user.username", "=", username)
.get();
if (!credentials) {
throw new Error("Invalid username or password");
}
const validPassword = await argon2id.verify(
credentials.hashed_password,
password
); |
Beta Was this translation helpful? Give feedback.
-
As someone who currently uses Lucia (before I was using Next Auth but faced way too many issues & didn't get answers for it & couldn't figure it out myself either), I like Lucia bcz its simple. My opinion is simple: Just do what is simple & common (popular choice) Lucia meant you had to write your own db which is a bit hard but still felt much simpler than Next Auth. So I'd prefer if you keep it simple as it is & prefer common use-cases even if you don't agree with them. You can create another project if you want to propose a new solution. See Express vs Koa but even though Koa is better as the creator of Express himself said, its still not popular. Express works. For everyone. And even now Express is used much more than Koa. So you can make a better yet another library & it has to be a massive 10x improvement for people to switch. But the alternative is people will stop preferring Lucia if it gets hard or if you add something that makes something that was easy previously hard. If you think you can execute your vision, go ahead. Early adopters can beta test. Dont pull an Angular 2 or what Next.js did recently. Keep it simple. We just wanna get Auth working with simple APIs. Don't wanna learn more than that. I think most devs are like that. If we could do a Clerk/Auth0 style 1-liners then we'd prefer that but there's no OSS library that provides it yet :) |
Beta Was this translation helpful? Give feedback.
-
This may be slightly off-topic, but just thought I'd mention what I'd love. In my current work environment we have a combination of apps. Mainly C# WinForms client app and WebForms, Angular and Qwik web apps. I would love to be able to unify the authentication and authorization between all of these in a central place. Obviously this is probably way beyond the scope for Lucia and I totally get that. I think you had previously mentioned at one point about maybe having options for other non-js based frameworks and that is something I'd love to see. Take this as you will, I certainly don't have any expectations. |
Beta Was this translation helpful? Give feedback.
-
Been reading the discussions, I think rip off the band aid move might be the better approach. When projects try to do too many things and cater to many people the project just gets bloated from wat Ive seen. |
Beta Was this translation helpful? Give feedback.
-
I think there are a number of upstream decisions to consider. 1. OsloWhat is the niche of oslo? What problem(s) does it solve, and for who? When should someone be reaching for Oslo as the best choice? My impression is that Oslo provides primitives, but leaves it to the consumer to put them together in a useful way. 2. LuciaWhat does Lucia aspire to do beyond Oslo? What needs to be true for Lucia to be the right choice for a project? My mental model for Lucia is that it provides an unopinionated abstraction for authentication & session management that is built upon the basic primitives (of the sort I expect Oslo to provide) and has adapters/flexibility to support whatever (JS/TS) framework and database combo that I am using. So personally I would expect it to have APIs/guides for everything I would need to correctly use all the supported authentication methods. 3. Lucia capabilitiesWhat authentication methods does Lucia aim to meet? There are a number that have been discussed already and seem aspirationally in-scope:
Given my impression of Lucia and what's in scope for authentication methods, I would like see better support for some of the credential scenarios. I think it would be reasonable for lucia to have opinionated APIs for validating a password is secure enough (length, character mix, etc.), generating an email verification code, validating the verification code, and password resets. I would additionally like to see 2FA/MFA as potentially in scope - something akin to Covering lucia's full surface area with these things is a big undertaking (frameworks x db adapters x authentication methods) so perhaps some of this should move upstream to Oslo utilities? 4. Given the separate aspirations of Oslo & Lucia, what's the right abstraction?What are the right building blocks to support lucia's aspirations, and how do they differ from Oslo? I think removing keys and providing an answer that is akin to "pick one of these 4 things" fails to meet the high bar set by Lucia thus far in creating an easy to use auth solution. Keys are an effective way to grow with the consumer, who can easily add additional auth methods as it makes sense to in their app. Doing so doesn't require major changes. For example, does it make sense for password hashing to not be exposed by Lucia at all, and for that capability to move to Oslo? Still allow overriding it in Lucia, but if you want the lower level functions for tasks like hashing a password or generating a 2FA code, require taking on Oslo as a dependency to get them? Ultimately I think until you have a clear vision of what you do want ("removing keys" doesn't count!), it'll be a struggle to make the right choices. As a Lucia consumer, I'd be open to the migration work of keys being removed, but I'd want a clear and compelling value prop of what I'm getting out of the deal. Is it:
My impression of your post is that you want to remove keys because it's a lucia specific concept. I've already learned it, though, so getting to keep a unified API based on a concept I've already learned is a good deal for me. So currently it's hard for me to support removing something that I've already learned which gives me an easier API. Can you instead paint a picture of how great my life is going to be as a result of giving up keys? |
Beta Was this translation helpful? Give feedback.
-
My personal opinion is that, especially in the long term, there is more value for Lucia to focus on documentation and guides, rather than the code itself. What does that mean? Rather than providing database-connected APIs for verification tokens, OAuth, passkeys, etc (ie. do 90% of the backend for you), it'll be more beneficial if we teach people how to implement auth using Lucia, Oslo, and their preferred ORM or query builder. Like, for OAuth, you just need like a few simple database queries. It takes minimum effort. You should be able to do that. And there lies the issue. If you want to implement auth by yourself, you should be ready to work with your database and put some effort into it. Lucia and Oslo will take care of the details, like session expirations, passkey validation, OAuth requests, and more. But you should at the very least understand the big picture. How OAuth works. How passkeys works. And the docs is the best place to teach that. Auth isn't easy. A library can only handle so much. And IMO we shouldn't hide that fact. |
Beta Was this translation helpful? Give feedback.
-
What you are writing makes a lot of sense @pilcrowOnPaper. I am eager to see where it will go. My 2 cents: I think there is a big market for a low profile, generic and well documented solution. That said, on the other side of the spectrum you have - I believe - a big market for a full-fledged open source implementation which is - with some config and your own frontend - ready to ship. The downside is that it will be an opinionated solution, since you will make this for one set of libraries+framework to keep it maintainable. That said, with good documentation, this can be a very valuable example implementation for the community? My company would be happy to invest/donate in both if you are interested in such a plan. (I hope its clear enough, Its a bit tricky to be sure its well understandable 😄 ) |
Beta Was this translation helpful? Give feedback.
-
When we started to search for Auth Frameworks which support Vue + Nuxt, we didnt find many, which are production ready. There were some but they are bugged a lot - so not really safe to use. |
Beta Was this translation helpful? Give feedback.
-
@pilcrowOnPaper So is there going to be a huge change to Lucia? Was planning on investing some time in figuring out how to implement Lucia with Next.js 14 since I am tired of wasting my time trying to do a work around to get next-auth sessions to be stored in the database. |
Beta Was this translation helpful? Give feedback.
-
@pilcrowOnPaper first of all, very nice of you to start this thread! As a somewhat experienced but still noob developer, here's my opinion on the subject. (Keep in mind I consider myself naive on the subject of auth. Even if i've been coding for some years, I have very little professional experience.) Why I loved using Lucia?
Migration to an eventual v3Breaking changes?I would not care at all migrating (to a keyless implementation or whatever) if there is a guide provided. Since Lucia's doc is understandable, I have no doubt a migration guide would be perfect if you decide to go this route. Development / MaintenanceI would also be happy to migrate if it means you (as the main maintainer of the project) think you have good reasons for it, and it also means that you'll be happy to maintain it further. On the other side, i'd be pretty sad to learn that you would deprecate it after v3 because it went too far from you vision. It is important that you respect youself in the process. On the subject of Guides / TemplatesIn my experience, the hardest part was to figure out the auth (sign up, login, logout) workflow. Once all the bits put together, adding a database query here to create a user or verify a hashed password is just routine (let's be honest, i'm already making lots of complex db queries in my app...). Lucia really helped me pull off a simple template to build a mental model. Even just templates (or even maybe a checklist of crucial steps in order) without the library would have been a substantial help to me. What i'd like Lucia/Oslo to beOslo as primitive utilities is in its place imo. For Lucia, the best I can think of is something I can give a config to and that would return a collection of utilities to use around my app (much like it is designed now). I kind of think of it as a wrapper around Oslo for common tasks. I don't really care about details like database schemas or keys, as they are easy to pick up (and adopt) if it makes sense and make my dev life easier. I have trust in people like you to figure out the best approach, as I have never created an auth library as you did. Also, in my ideal world, all these common tasks could be overriden/extended to offer maximum flexibility. I saw someone mention Express/Koa in some comment and I think it is a nice way to think about it (mostly middlewares). Also SvelteKit architecture with load functions, actions and hooks is an awesome model for me. Perfect balance between opinionated (optimal patterns and workflows) and flexible (specific db queries, validation functions, sending TOTP requests to the user with X method as somebody else mentioned). Another rather random example, but I love the way svelte-headless-table implemented the 'plugin system*, making any instance unique and extensible while providing multiple possibilities and combinations. Library vs FrameworkNot an expert, but I sometimes see the debate of "is React a framework or library?". I don' t use React so I don't know at all, but my point is more about the comparison. From what i've seen : Library = you call it in your code (Oslo?) Framework = wrapper that call your code at the right moment (Lucia?) This is how I see things from my naive point of view! Hope my take on this can help you clarify the questions that are going around your head! ✌️ Edit : To add up on that, i'm also really fan of Vanilla JS solutions as it usually doable to use a JS solution in any framework (with a little bit of work of course) but much harder the other way around (ex: port a React lib to Svelte). |
Beta Was this translation helpful? Give feedback.
-
I know how to do database queries or fancy algorithms coding, but I don't know how to maintain an authentication flow that is actively up to date with the current best practices or at least the current standards of security in the given scope. The latter is the reason why I look for existing auth libraries for early stage projects, and what I would want from Lucia to provide. |
Beta Was this translation helpful? Give feedback.
-
My 2 cents:
My reasons for using Lucia:
IMO focus should be on a robust library that is forward compatible to always use best practices on account security and session management. Great work BTW, and thanks a lot! My first production app using Lucia is going online these days! |
Beta Was this translation helpful? Give feedback.
-
Just found this library. Wish I found it earlier. Amazing work. As others have already stated: Just keep it simple and maintained. No need to add complexity. And yeah, passwords are critical for most applications and they are not going anywhere, so built in utilities to help with password implementation would be helpful. What I'm confused about is that your guides explain very well how to set up password properly with email verification, preventing throttling etc. What is the issue exactly or am I missing something? What more needs to be done? Assuming you are not operating some financial institution, won't most apps just work fine by following those guides? |
Beta Was this translation helpful? Give feedback.
-
Certainly agree with this. If password auth is such an issue, why not make it CLEARER in the docs how to implement this properly and what steps to take instead of actively discouraging it and limiting the docs on this topic. And they can say as much as they want they arent purposely limiting the documentation on this topic, but they are by only including the base monimum required to make it work... In some cases not even that. Would rather like to see more complete docs. They only whay devs will implement this properly is by the "experts" who offer the library educating, especially new devs, how to do it properly. In some countries, email and password is the only option for auth. But by limiting the docs and access to this feature, these devs end up implementing auth strategies that are unusable in their target markets... Typically github auth in tuts, my users dont even know 99% of these auth providers. Anyway... Find it hard to learn and get better as a dev from the docs since there is little info there to help you in the right direction. Completely reliant on some guy on YouTube feeling like tackling this and making a video to help other devs with whatever since no one else will. |
Beta Was this translation helpful? Give feedback.
-
It will be interesting for me to watch how this project evolves. Having the ability to bring and swap out pieces as needed is a great feature. |
Beta Was this translation helpful? Give feedback.
-
Hi, everyone.
The vibe I'm getting recently is that there's a disconnect between what I want out of Lucia, and what the community wants. An identity crisis if you will. I'm not sure how big the gap is, but it's definitely there. Part of the reason for that, I believe, is that while I have kept the project scope limited, I haven't clearly defined the goals of the project. I see that as a failure on my part as project lead. And my view on the library has definitely changed over time. The issue needs to be addressed, so let's talk about it.
Personally, I don't see auth as a whole hard. Just tedious and time consuming. So I don't want Lucia to handle a lot of things, just the annoying bits. And the most obvious target I see is session management. I think you'd agree here as well. And database adapters are a nice addition for small~medium sized projects too. So Lucia should definitely handle sessions and related database queries. Good.
Then comes the issue with authentication. There are mainly 2 approaches: OAuth and passwords. Either way, you interact with keys right now.
OAuth
First OAuth. I think having built-in providers that do all the requests is a big time saver. But the OAuth story in Lucia doesn't end there. You create a table for storing keys, and Lucia will handle database queries for getting existing users and creating keys. Is that necessary, and more importantly worth it? Yes, you get a nice API interface, but you give up on how those data is stored and you have to learn the concept of keys. Like, here's an example. The first one is with keys, and the second is without.
There are both equally readable and maintainable. And with the second one, you don't have to learn a Lucia-specific concept. Another benefit is that without the limitation keys, you have the options to store data that best fits your application:
github_user
tableThese are better options than storing every method into a single table.
Passwords
Next, password based auth. Unlike Auth.js, I don't want to make it harder to implement it. But I do want to discourage people from implementing it if they're not willing to put the care required to pull it off. Because here's the thing: email/password auth is tedious. It's not hard, but there's a lot of steps required to implement it correctly. And you're not making your password based auth any more secure by using Lucia's keys. Besides, if you're willing to take time to implement email verification, password reset tokens, and login throttling, keys aren't really making your work significantly easier.
You might think Lucia should handle stuff like verification tokens and login throttling. But, auth is inherently tied to your database, framework, and project structure. Lucia isn't the right abstraction level to implement it. For it to be realistic, Lucia would have to only support a single database driver/ORM, and maybe a single framework.
And similar to OAuth, if you just want basic password based auth for internal projects, it isn't hard:
The biggest benefit you get right now is that Lucia hashes password using the recommended algorithm + settings. But Lucia/Oslo can just provide an API just for that, without the database part or keys.
What do I want?
I want to remove keys. Maybe it's too radical. But I think it's a flawed concept that just isn't worth the trouble.
My original plan with v3 was to keep keys but only for OAuth, but maybe it's better to change rip off the bandaid.
But that's what I want from Lucia, taking into considering what's possible (session management) and what's not (a perfect library that handles every step of auth). I have to accept that it's not a personal project anymore. And that people hate breaking changes. So I want to ask the community; what do you want from Lucia?
Beta Was this translation helpful? Give feedback.
All reactions