outline icon indicating copy to clipboard operation
outline copied to clipboard

Local Authentication

Open iojanis opened this issue 4 years ago • 108 comments

I don't like the fact that you need a Google or Slack account. I'm sure you'll scare off a lot of people with that. Have a lot of interest in this app, but would never think of hosting something myself, but still have the users go through another service.

iojanis avatar Feb 12 '21 13:02 iojanis

#1183

matbrgz avatar Feb 12 '21 17:02 matbrgz

#1177

It should be as simple as :

  • Generate an admin user:password in the CLI while starting for the first time
  • When the admin logs into the web, have that password changed forcibly
  • Allow admin to setup whatever authentication methods he needs from the web console
  • Additionally, there should be a non smtp based adding of users too. ie the admin generates a user:pass pair which is given to a 3rd party in whatever secure channel he prefers to. On that user's first login, have that password forcibly changed so the admin cannot snoop in.

Voila, a fully self-hosted instance with no external dependencies like web-oauth or smtp.

arjunv avatar Apr 18 '21 05:04 arjunv

Looking at passport.js and the current providers, it seems a local datastore would be needed to store/validate the local users. Unfortunately, passport doesn't seem to provide this (only verification). So the effort would be:

  • Create a schema for user credentials in the DB (with sequelize)
  • A command line for create & delete an user with optional admin rights.

From this point on, the community could test this functionality. The UI for password reset/change can then be added at a later step.

Allow admin to setup whatever authentication methods he needs from the web console

This is already done with env variables if I'm not mistaken.

slurdge avatar Apr 18 '21 12:04 slurdge

I don't see the CLI as being needed, the first ever sign-in on self-hosted automatically creates the team and becomes admin already. I see something like this as a bare minimum set of functionality, you can't have password auth without the ability to reset passwords...

Backend

  • Allow for password auth to be turned on or off via ENV in the same way as all other authentication integrations
  • Add a new password column to user_authentications,
  • Create a new password strategy inline with the existing three for password auth, it will need to handle password being passed to accountProvisioner and userCreator commands
  • For user creator we'll need a reasonable default for a minimum password length validation
  • Add /auth/password.reset endpoint for requesting a password reset with email
  • Add rate limiting for signin and password reset endpoints
  • Add email for password reset with one-time use JWT
  • Tests for all of the above

Frontend

  • On the login screen password auth will be an exception and need it's own special-case UI component
  • Hook up login and password reset UI to API's from above
  • Ensure error cases are handled correctly in the UI, eg wrong user/password combo – as we've typically gone through SSO error handling has been different through return query params, in this case it'll be over XHR.

tommoor avatar Apr 18 '21 17:04 tommoor

Thank you for this nice explanation.

I was including command line as I was envisioning a scenario where email SMTP settings weren't provided and the person installing would just like to have a user account for himself or herself, and thus would create it using the command line (probably through docker).

However if using the existing SMTP/email infrastructure make it easier to create and maintain, this is probably much better. Is there anything I can help with ?

slurdge avatar Apr 18 '21 19:04 slurdge

Hello,

I'm trying to have a take at making local auth a reality, but I stumble upon difficulties in the implementation. Is it best to ask it here or to open a discussion on Github ?

slurdge avatar Apr 30 '21 16:04 slurdge

This is good to ask! Feels like filing a draft Pull Request against this repository seemed a good use of GitHubs discussion features around code changes.

Given we are in good exchange here, what is it that concerns you?

On Fri, 30 Apr 2021 at 18:29, slurdge @.***> wrote:

Hello,

I'm trying to have a take at making local auth a reality, but I stumble upon difficulties in the implementation. Is it best to ask it here or to open a discussion on Github ?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/outline/outline/issues/1881#issuecomment-830212624, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAMRV7EBRNI5HHTFZYTSFELTLLLIFANCNFSM4XQW4LZQ .

almereyda avatar Apr 30 '21 19:04 almereyda

I'm trying to follow the steps outlined by @tommoor above, but I'm not fluent in the framework used. I've import passport-local (did not touch the DB yet), and tried to make a new "local" provider with the following rules:

// @flow
import passport from "@outlinewiki/koa-passport";
import { Strategy as LocalStrategy } from "passport-local";
import jwt from "jsonwebtoken";
import Router from "koa-router";
import passportMiddleware from "../../middlewares/passport";

const router = new Router();
const providerName = "local";

export const config = {
    name: "Local Account",
    enabled: true,
};

const strategy = new LocalStrategy(
    function(username, password, cb) {
        console.log("getting username...")
        return cb(null, { name: username });
    })
strategy.name = "local";
passport.use(strategy);

router.get("local", (ctx) => (ctx.body = "OK"))

router.post("local", passport.authenticate(providerName));

router.get("local.callback", passportMiddleware(providerName));

export default router;

I also modified Providers login to have a special case of form if the provider is "local", which post to "/auth/local"

However I can't hit the callback in the strategy, I always have an error when I hit the route router.post("local", passport.authenticate(providerName));

My plan was first to be able to have a fake user, then have a real user with a manual password in the DB, then go from there.

slurdge avatar Apr 30 '21 20:04 slurdge

Hi there! Any news?

n-rodriguez avatar May 29 '21 23:05 n-rodriguez

Ugh. Guys. Sorry for the unproductive comment, but It's such a bummer that something with such great potential is essentially made obsolete by something so silly.

solarkraft avatar Jun 02 '21 19:06 solarkraft

I did a bit more try, but without a hint in how to go in the right direction, I fear it would take me quite a bit of time.

slurdge avatar Jun 02 '21 20:06 slurdge

I would also like to use this self-hosted with an authentication other than slack or google, but have seen zero documentation on it, just various issues referencing things, most of which are not clear whether they are in the current release or not (due to lack of documentation and examples).

If it's the intention to drive more people to use the hosted version, it isn't working, as it wouldn't work in my environment.

umdstu avatar Jun 03 '21 17:06 umdstu

I would also like to use this self-hosted with an authentication other than slack or google, but have seen zero documentation on it

Hi – there's no documentation because it's not a feature that has ever existed in the product. This issue is a request for it to be built.

tommoor avatar Jun 04 '21 15:06 tommoor

Ah ok. It gets quite confusing, there are several tickets referencing it, and most closed, and it's not clear whether things were closed as duplicates, consolidated, or something was finished and merged in.

But that makes sense, thanks for clarifying. Is there a roadmap (if so, is this on it?), or is this goin to be something only in the paid enterprise version, like LDAP support?

Thanks!

On Fri, Jun 4, 2021 at 11:41 AM Tom Moor @.***> wrote:

I would also like to use this self-hosted with an authentication other than slack or google, but have seen zero documentation on it

Hi – there's no documentation because it's not a feature that has ever existed in the product. This issue is a request for it to be built.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/outline/outline/issues/1881#issuecomment-854823536, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJ445H7U57LH4LEMJIC5ZDTRDXYZANCNFSM4XQW4LZQ .

umdstu avatar Jun 04 '21 16:06 umdstu

I saw in the project issue discussion that the custom authentication function may be launched as a commercial function. (Please also point out if I understand it wrong)

As a software developer, I think if this is true, it might be a good thing for the development team. After all, income can make the team survive and make the software develop better.

However, if the official permission is allowed, I think I can submit a pull request to allow outline to run completely offline, free from various third-party OAuth (slack, ms, google), and let it serve individuals or small teams.

I hope it will not affect the planning of the outline official team. If the official team has a clear plan, I also hope that it can be clearly stated in the issue what kind of functional implementation will be merged.

BTW I recently tried to complete the self-hosted deployment without changing the outline, and found that the software performed very well.

Thanks.

soulteary avatar Jul 09 '21 06:07 soulteary

@soulteary , I tried to have a local auth backend but hit a bump somewhere on the road. My understanding is that having a local backend is ok, just that developers have understandably other priorities. Did you already progress on this ?

slurdge avatar Jul 16 '21 16:07 slurdge

At the time of posting, I had finished and used this software for a week. In fact, there is no need to change much, especially for small teams or individuals. @slurdge

At present, I only use declarative configuration to solve authorization, and I have not developed an independent authorization management interface, deauthorization interface, and interface to modify specific member information.

I believe that after sharing, anyone can quickly develop the authentication functions they need, such as SAML, LDAP, ANY OAUTH, and independent mail login functions, but this may be related to the concerns mentioned in my previous post.

In the previous discussion, I saw the hard work, business planning and opinions of the official team for self-deployment and certification.

  • https://github.com/outline/outline/pull/1953
  • https://github.com/outline/outline/issues/938

Therefore, I think in order to respect the development team and protect the future development of the outline, it is best to obtain authorization to do so.

soulteary avatar Jul 16 '21 23:07 soulteary

@soulteary It's exciting to hear about that you've done. I'm looking forward to being able to use this with a team I'm currently working with. Regarding the development team, per chain you linked to, specifically this comment, they are not opposed to the community developing such functionality, and in fact have encouraged it. Doing so should not take away from an Enterprise feature they offer, in my opinion. They likely have no plans to include it in outline's core code, and that's reasonable, but having it as an add on module, middleware, or whatever you'd like tocall it, seems like fair game both in spirit and and documented desires. I would say publish you're branch, let folks do with it what they wish, or go even further and make it more modular as a separate repo of just the non-outline base code. I'd be willing to help with this if you'd like.

But that's just my opinion of course. Happy coding.

umdstu avatar Jul 16 '21 23:07 umdstu

Thank you for sharing your views. This week I used Outline to replace Confluence, which I have used for more than two years. It is very pleasant. I also hope that more people can easily use this great software.I believe that after more people use it, this software can develop better, and for users, they can get more benefits.

Regarding my concerns, I believe I have made it clear above, let us wait for the official response.

By the way, privatization deployment is actually only the first step, and the follow-up may also involve content migration, attachment management, some localization changes, more resource display, and so on. In the following post, I mentioned what I did during use and what I planned to solve.

https://github.com/outline/outline/issues/2308

soulteary avatar Jul 16 '21 23:07 soulteary

hello @tommoor , could you help us clarify the official position regarding this ?

only use declarative configuration to solve authorization

If it's only a matter of configuration files for individuals, it would be easier to integrate in the base IMHO.

@soulteary From what I understand the architecture is already pluggable with any auth you may want, it's just it will not be provided in then open source package (see Microsoft ticket).

slurdge avatar Jul 17 '21 09:07 slurdge

only use declarative configuration to solve authorization

Honestly I'm not too interested in supporting a solution like this, it feels like a kludge that would end up being a perpetual maintenance burden. How are you handling passwords with this configuration-based setup?

tommoor avatar Jul 18 '21 15:07 tommoor

It's a real bummer that something as basic as local user authentication isn't there, especially since on the frontpage of the main website you see this:

"Outline’s source code is public, and the editor is open source so the community can help improve it too. Prefer to host on your own infrastructure? No problem."

Google Authentication isn't hosted on my own infrastructure. Not no problem. Big problem. We're not talking about a plugin here, but core functionality. Disappointing, I was really looking forward to self-hosting this, but I'll have to pass.

At the very least, the frontpage of the website should be updated to remove that last bit from under the "Open source" card, if local authentication will never be implemented, because it implies the solution in its base entirety can be self-hosted (which inherently means without external dependencies, right?).

wpuckering avatar Jul 22 '21 20:07 wpuckering

It's a real bummer that something as basic as local user authentication isn't there, especially since on the frontpage of the main website you see this:

"Outline’s source code is public, and the editor is open source so the community can help improve it too. Prefer to host on your own infrastructure? No problem."

Google Authentication isn't hosted on my own infrastructure. Not no problem. Big problem. We're not talking about a plugin here, but core functionality. Disappointing, I was really looking forward to self-hosting this, but I'll have to pass.

At the very least, the frontpage of the website should be updated to remove that last bit from under the "Open source" card, if local authentication will never be implemented, because it implies the solution in its base entirety can be self-hosted (which inherently means without external dependencies, right?).

You can use RedHat KeyClock to create your own oauth server, then you can use passport to implement your own strategy.

In my opinion, Outline must never implement a local auth.

matbrgz avatar Jul 22 '21 20:07 matbrgz

You can use RedHat KeyClock to create your own oauth server, then you can use passport to implement your own strategy.

In my opinion, Outline must never implement a local auth.

A very disappointing answer, a very disappointing project...

n-rodriguez avatar Jul 23 '21 12:07 n-rodriguez

I totally understand not wanting to have a built in user system. It supports oauth I'm an indirect way I'm told, set up an oauth server?

On Fri, Jul 23, 2021 at 8:30 AM Nicolas Rodriguez @.***> wrote:

You can use RedHat KeyClock to create your own oauth server, then you can use passport to implement your own strategy.

In my opinion, Outline must never implement a local auth.

A very disappointing answer, a very disappointing project...

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/outline/outline/issues/1881#issuecomment-885604774, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJ445B5LOT5YSAWZHN5WQTTZFOGJANCNFSM4XQW4LZQ .

umdstu avatar Jul 23 '21 13:07 umdstu

A very disappointing answer, a very disappointing project...

What's wrong with this... it's the sad reality

n-rodriguez avatar Jul 23 '21 20:07 n-rodriguez

What's wrong with this... it's the sad reality

I think a lot of people would disagree with that. Outline is a great product and even more, it's open source which is totally not a given when you see the level of dedication devs have to put into it. Also, from a pure "issue tracking" point of view, your comment didn't add anything. The issue is so see the feasibility and implementation of local auth, and you'll notice the devs didn't close it.

When I have more time, I'll try to do another shot at it to have a solution that would please most people (myself included, after all, itch scratiching & all that).

slurdge avatar Jul 23 '21 21:07 slurdge

it's open source

It's your vision of open source, see https://github.com/outline/outline/issues/1881#issuecomment-885195057

when you see the level of dedication devs have to put into it

No doubt about it, the screenshots speak for themselves

The point is : I dont think installing keycloak is a good solution especialy for small teams and saying

Outline must never implement a local auth.

Is just... disappointing

n-rodriguez avatar Jul 23 '21 21:07 n-rodriguez

I have made some progress in the implementation of passport-local.

However, after the initial passport authenticate , I'm having a hard time passing the user to the signIn method. I suppose that in outline, we should use signIn and not session or log in through the context.

I'm doing something like this (putting async with ctx & next doesn't change anything, we can call auth directly):

router.post("local", async (ctx, next) => {
    return passport.authenticate(providerName,
{
    session:false,
    failureRedirect: '/',
    failureMessage: true,
})(ctx, next)},  function(req, ctx) {
    log(`req : ${JSON.stringify(req, null, 4)}, ctx: ${ctx}`);
    signIn(
          ctx,
          req.user,
          req.team,
          providerName,
          true,
          true
        );
});

But req.user is always empty. I also tried to have a successRedirect but in that case passport tries to serialize the user and outline does not define the serializeUser functions.

Questions:

  • Should we implement serializeUser ?
  • Is there another way to do the signIn after that passport is called ? The authentication function is calling done with a proper user, so it should be done afterwards, but I can't understand how to tell koa-router and koa-passport to do just that.

slurdge avatar Jul 29 '21 09:07 slurdge

Is it feasible to support JWT authentication like how grafana does? From the perspective of a user with multiple self-hosting services, this workflow seems reasonable, as all user identities are managed via a unified provider, and all authentication policies are managed via a single source. It works like this:

  • Upon each request, a reverse proxy (typically traefik) forward the request info to a authenticator like pomerium, which verifies the user identities (by inspecting cookies).
  • If the request is not authenticated, pomerium would return a login URL, the reverse proxy then redirect the user to login via OAuth2.
  • If the request is authenticated, pomerium looks up a list of policies to see if this user has permission to access, if so, the request is permitted, the reverse proxy passes the request to backend service. Some JWT headers will be attached to the request.
  • Grafana extracts user identity (who is accessing?) from the JWT header, and also verifies it by inspecting the signature (yes, it knows about the public part of JWT signing key). The request is then considered authenticated in Grafana.

Of course, I know this could be too complicated for Outline for now. After all, you still need the user identity (username or email addres) locally stored and known to Outline.

JokerQyou avatar Aug 04 '21 06:08 JokerQyou