New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simpler UX for OAuth2 login with GitHub #3837

Open
solderjs opened this Issue Apr 23, 2018 · 36 comments

Comments

@solderjs
Copy link
Contributor

solderjs commented Apr 23, 2018

UX Review: Current Behavior

Long Read

I have enabled OAuth2 for GitHub

I’ve also changed my theme so that Login with GitHub is prominent.

When a user clicks to Login with GitHub (or any OAuth2 provider) for the first time, they are dropped at a screen that asks them to login or create an account.

I’ve watched people then attempt to sign in using their GitHub username and password because it isn’t clear to them that they’re continuing an account creation process.

I think that this could be simplified if the Login / Create Account page were laid out side-by-side, or if the order were reversed.

Better yet, have them enter their email or login, then click “next” and then, based on whether or not that account exists, present them with the appropriate “login” or “create account” screen.

I’m going to try and get some help from a friend of mine who does frontend to modify the theme and provide a suggestion and potentially a pull request.

Templates for Reference

Potential Conflicts

The "inner" templates are also used on these pages:

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented May 11, 2018

For reference, this is the screen that has confused several people:

screen shot 2018-05-11 at 3 35 34 pm

@lafriks

This comment has been minimized.

Copy link
Member

lafriks commented May 11, 2018

Any suggestions what to change?

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented May 11, 2018

I'll post back with another screenshot once I modify the template.

Although it'll be quick to modify the template as an interim solution, I think the best solution will be to change the flow - similar to what Google, Slack, and others do:

  • 1a. Let the user know that they were successful: Thanks for signing in with [[sso provider]], [[username]].
  • 1b. Ask the user for their email address: "Please input your email address:"
  • 1c. The user clicks "Next".
  • 2a. On submit check if the user exists or not, then display the appropriate options:
  • 2*. (some claim this is a security vulnerability, but that's bogus as you can test for the existence of the user in many ways and the the user isn't private anyway)
  • 2b. Ask the user for their password depending on whether or not they exist in the system
    • "Please enter your [[sitename]] password" (as to avoid confusion with their [[sso provider]] password).
    • "Please create a password for [[sitename]]" (or, for security, let people disable passwords for security and set up 2FA instead - such as "QR Code" or "Magic Link"). Also display the captcha.
    • Or "Enter or create your password" if the "security" measure of not being able to probe an account name is being held to, and only show the capcha afterwards
  • 2c. The user clicks "login" or "create account"

@solderjs solderjs referenced this issue May 12, 2018

Closed

repo moved? #125

@larshp

This comment has been minimized.

Copy link

larshp commented May 12, 2018

Suggest not asking the user for setting a recovery password. If signing in with GitHub, I guess GitHub is responsible for the recovery process if needed?

OAuth is a great way to get rid of passwords for smaller sites.

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented May 18, 2018

@larshp The problem is that github routinely goes down from DDOS attacks.

That's one of the benefits of using a self-hosted git solution.

What we really need is federated authentication, but that doesn't exist yet. There's no financial incentive for a large company to develop an open protocol (i.e. email) that a competitor could use. Instead, protocols with built to preserve vendor lock-in (like OAuth2) ensure their creator's survival (i.e. Facebook). Then there are some valiant efforts from non-profits like Mozilla, but the solutions that they come up with are too politically charged (i.e. ads are evil, non-anonymity is evil, etc - more about why it's politically morally superior than the benefit to the user) and/or geeky (requiring manual steps, like OpenID) to make it into mainstream.

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented May 18, 2018

For reference:

My friend didn't quite understand the purpose of the current flow either. I explained it to him like this and he understood it better:

You’re half-logged in. You are “authenticated”, but not “authorized”.

You’ve gone through the TSA security check, but you have to do a gate check at the correct gate to be boarded on the plane.

The service knows that your ID is a valid ID, but it doesn’t know what that ID is good for.

Like it can see you have a Utah Drivers License, but it doesn’t know what your phone number or email address is, so it still can’t connect with you.

Or as if you called up the electric company and told them your passport number, and you have an account with them, but your passport number isn’t actually on file so the still need your name and account number even if they validate your identity by the passport number.

The question remains “which things on this service belong to you?” and by linking the account you’re saying “oh, you’ve seen me before, but you knew me by my email, not my GitHub name. These things by this email are what belong to me” and if you create a new account you’re saying “nothing, this is my first time here, so let’s get my ID linked to a new account”

In either case, next time around it knows what your GitHub ID relates to on the local system.

@aaronpk

This comment has been minimized.

Copy link

aaronpk commented Jun 4, 2018

What we really need is federated authentication, but that doesn't exist yet.

This sounds like a great use case for IndieAuth. https://www.w3.org/TR/indieauth/

IndieAuth is an OAuth 2.0 extension, which avoids the centralized problems with existing OAuth solutions by using DNS for "registration" of client IDs and user IDs. Every user account is identified by a URL (for Gitea this could be your Gitea user page), and client IDs are also URLs (would be the Gitea instance home page in this case.)

To log in to your Gitea instance, I would enter my own Gitea profile URL. Your instance would then do discovery on my URL to find where to send me to authorize the login on my own OAuth server (my Gitea server), which would then send me back to your Gitea where it would be able to verify the authorization code against my Gitea instance.

I'd be happy to walk through this in more detail if you're interested!

@tantek

This comment has been minimized.

Copy link

tantek commented Jun 5, 2018

I think IndieAuth makes a lot of sense as the way to implement a federated login protocol to provide a "simpler UX for OAuth2" login for Gitea as this issue is named.

It would also be possible to provide support for the "with GitHub" portion (as originally noted in the issue) without having to ask / wait for GitHub to implement IndieAuth, by adding https://indieweb.org/RelMeAuth support.

https://indielogin.com/ is an example of an open source service that supports both of those, IndieAuth as @aaronpk suggested and RelMeAuth, and is in daily use by folks signing-into the IndieWeb.org site.

(Originally published at: http://tantek.com/2018/155/t1/)

@clarfon

This comment has been minimized.

Copy link
Contributor

clarfon commented Jun 30, 2018

I ran into this today and I find it very confusing that oauth requires users to type in a password and captcha for their account. Additionally, clicking the "sign in with ___" button makes it unclear whether I'm going to have the option of linking with an existing account.

IMHO, we should be able to disable captcha and password requirements for oauth-created accounts, so that you can have an empty password need be. It might also be beneficial to allow clearing passwords if you use oauth, so that you really don't have a password managed by your gitea account, and just from this external provider.

@jhabdas

This comment has been minimized.

Copy link

jhabdas commented Jul 6, 2018

@coolaj86 how would you describe IndieAuth to your friend?

@aaronpk

This comment has been minimized.

Copy link

aaronpk commented Jul 8, 2018

Here's a post I just wrote explaining IndieAuth and how it solves a number of the challenges with OAuth in this context.

https://aaronparecki.com/2018/07/07/7/oauth-for-the-open-web

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Jul 10, 2018

@jhabdas I wouldn't.

Total tangent, but here goes...

All Authentication Sucks

For years I've been working on a protocol I call OAuth3 (but I'd like to come up with a better name). It's like a peer version of OIDC or a convenient version of IndieAuth.

However, there have been a number of roadblocks and challenges. Having something work peer-to-peer (decentralized) and be convenient, is neigh unto impossible (but it is possible).

The problem with all existing auth solutions is that they're in one of two camps: Proprietary/Centralized (OAuth2 implementations) or complicated for a non-technical user OpenID/IndieAuth.

What Could Succeed

The way I believe that peer authentication will succeed is by providing a solution that primarily executes on the client device (keypair authentication) but, by default, syncs the public keys to a shared server (with a centralized well-known server as the default), and uses a static page (or 3rd party app if on a phone) - no server APIs required to function - in essentially the way a browser plugin (or native Facebook/Google OAuth on phones) works to manage the exchanges.

Ultimately, browsers need to adopt such a protocol, but it most be open and compatible between browsers. The same is true for smart phones. Until then, the centralized server would serve as a "broker" to serve the user's preferences to other client-side apps.

Most importantly, people need to be able to login like they're used to. It must be easier than the current solutions, not more difficult (and how better to make it easier than by removing the most frustrating - and least secure - part of login: passwords).

What it might look like

First Login on a Device

  1. I click "Login"
  2. A pop-up to "generic-auth.org" opens up (better branding, but you get the idea)
  3. The user types in their email address (or domain name)
  4. The part after the '@' (if email) is checked for a /.well-known/peer-auth/index.json
  • on 200 OK, redirect to that with ?subject={email} for the next step and save the user's choice
  • on 404 continue using generic-auth.org
  1. I get a push notification on my device (or an email with a "magic link")
  • also, generic-auth.org could link to facebook, twitter, google, etc (though a true peer-auth site could not as that would require having a developer account)
  1. I click the link in email (or the "Allow" option on the push notification) and I'm logged in (or, if using 2FA), I put in a passphrase or totp code
  2. I accept any grants (email address, photo access, etc) as needed
  3. I'm now logged in
  • The static app on generic-auth.com (or my custom auth site) caches that this site is allowed login

Secondary Login on Device

  1. Behind the scenes the app loads a hidden iframe or postmessage to generic-auth.org
  2. The app is either told to use generic-auth.org, or the peer auth the user supplied before
  3. The app is given a pseudonymous pairwise identifier and short-expiry token without any interaction
  4. The user is simply "logged in" with 0 interaction
  5. The app silently renews the token (completely client-side, no server implementation), as needed
  6. If necessary, a grants dialog appears asking for my email address, photo access, etc

Logout

  1. When the user clicks "log out" in any web app (or app) that is passed via iframe or postmessage to the client-side and all private keys are deleted.
  2. This means that client-side tokens cannot be renewed (as they were securely generated with client-side private keys)
  3. Likewise an iframe or postmessage is opened for each of the list of sites which have been granted a token, causing their short-lived tokens to expire immediatley
  4. The generic-auth.org server (or the peer auth server) are notified that they may remove "Chrome on Jon's Macbook Pro" (or whatever) and its public keys from the list of authenticated devices.

Security

  • No cookies, only tokens
  • Client-side token generation
  • Short-lived tokens
  • Optional (but default) sync of public tokens and of grants per app/site
  • Only Facebook has a properly secure login that could feasibly be used on its own as-is
    • Twitter, Google, etc do not support PPIDs and are therefore insecure by default and cannot provide any information that could be used for encryption, etc, as all of the ids are shared among all apps/sites

But That's not a PERFECT Solution!

If you're on one side of a chasm and you want to migrate a host of people to the other side, you need a bridge. Something like generic-auth.org (in the hypothetical scenario above) acts as that bridge. The migration would be complete when browsers and phones natively support open-protocol peer-based authentication that doesn't require 3rd party apps to bridge the gap anymore (like how Chrome has proprietary Google auth implemented).

Until that time you have the problem of OpenID/IndieAuth if you go for the "Perfect" peer solution - it requires too much knowledge and effort to intentionally use it. My mom is unlikely to create a website and link it to her other accounts with HTML tags.

The truly "perfect" solution then is the solution that people who don't care about technology and who don't understand the risks with their current login and authentication setups will adopt out of convenience, and perhaps a sense of individuality or pride, but certainly not for being the most technically correct solution.

The Catch-22

Peer-to-Peer can either be convenient for end-users or convenient to develop. It simply can't be both.

The saving grace is that if it's implemented in JavaScript and used in native apps through a web view, we only need a single implementation for the client-side and only 3 to 5 APIs for the server side (sync public keys, send email / push notifications, sync pairwise grants) - none of which require knowing anything about security or keypair signing and encryption - just opaque files, simple messages, and plain-old JSON.

@aaronpk

This comment has been minimized.

Copy link

aaronpk commented Jul 10, 2018

You've basically described IndieAuth, but using .well-known instead of a follow-your-nose approach.

Email addresses are URLs too. If you assume an http:// scheme for an email address, fetching http://aaron@parecki.com/ provides the server with aaron as the username. Try it out:

curl -i http://aaron@parecki.com/

I made that site send an HTTP redirect to my actual website, so I can log in with my email address anywhere that uses IndieAuth right now.

Supporting two-factor auth, or passwordless auth is entirely up to the IndieAuth server, as it's not part of the federation spec. In fact, I already have passwordless login to my website using a push notification to my phone which works with every IndieAuth client already because the clients don't care how I authenticate to my website, just that I do authenticate.

As for your point about the bridge, anyone can create a generic IndieAuth provider that provides identities to anyone who wants one and doesn't care about owning their identity. They enter generic.co/user1 as their URL when they sign in. It's up to that service to make actually authenticating users as easy as possible, whether that's by emailing a one-time link, setting up a mobile app for push notifications, using a regular password, or whatever.

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Jul 10, 2018

@aaronpk Perhaps I misunderstood and need to read carefully. Perusing https://indieauth.com/ what I got was "This is a reboot of OpenID that requires you to create a webpage and link to it".

Perhaps that's not at all related to "IndieAuth" that we're talking about here (I didn't read your article yet, but I will now).

@aaronpk

This comment has been minimized.

Copy link

aaronpk commented Jul 10, 2018

Yeah sorry for the confusion. I've started the (very slow) process of shutting down indieauth.com because I never should have launched a service with the same name as the spec. That site is not a good representation of the concepts behind IndieAuth.

Please do check out the blog post, it does a much better job of explaining the actual goals and motivations of the spec.

@pkit

This comment has been minimized.

Copy link

pkit commented Aug 14, 2018

I'm not sure what's going on here.
But current flow cannot be called SSO or "OpenID Connect support" by any margin.
The whole point in Oauth2 (OpenID) is to store all users in ONE place.
I.e. currently gitea does not support any auth other than local.

@lafriks

This comment has been minimized.

Copy link
Member

lafriks commented Aug 14, 2018

@pkit local user needs to be created anyway or otherwise how would you reference/add rights etc for it if it is not saved?

@daviian

This comment has been minimized.

Copy link
Member

daviian commented Aug 21, 2018

@lafriks I think it's not necessary to create a "complete" local user. E.g. the user doesn't necessarily need a password if he gives up using https protocol.
The Oauth user is still able to use ssh and can login with his external provider.

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Aug 28, 2018

There is no need for requesting a password to be created. It can be set to something random and later set via email reset if necessary.

This is what needs to be communicated:

This is your first time signing in as `foouser` via `github.com`, but `foouser` does not exist here.

Would you like to

* Create a new account `foouser`?
* Link your `github.com` account with an existing account?

What I think would be best, in all cases, is to ignore the password altogether and simplify to a single flow across the board:

[begin if oauth]
You're signed in as `foouser` via `github.com` for the first time.

We need to confirm your email address just this once so that we have a recoverable link to your account in the future.
[end if oauth]

What's your email?

Thanks, go click the confirmation link.

Again no password needed.

In fact, passwords only add vulnerability to a web application. Without a password a person's account can be compromised via email. With a password the account can be compromised via any number of publicly available username/password lists, and still via their email. Passwords are a weak link that corrupts any chain you put them in (unless the password is a secondary authentication rather than a primary authentication - such as requiring it to make account changes after 15 minutes of inactivity).

@lunny

This comment has been minimized.

Copy link
Member

lunny commented Aug 29, 2018

If no password enabled, user should be denied when clone or push via http/https

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Aug 29, 2018

@lunny that could be handled by showing the user a device code and instructing them to use git config credential.helper store (or by asking them to upload their ssh key right away).

Or they could create a password and be warned that a password reduces the security of their account.

However, the password issue as not as pressing or as easy to fix as the original issue that the current create account page is really confusing for people trying to login with GitHub.

@lunny

This comment has been minimized.

Copy link
Member

lunny commented Aug 29, 2018

@coolaj86 Yes, I agree with you.

@grothesque

This comment has been minimized.

Copy link

grothesque commented Sep 26, 2018

I tried try.gitea.io and I've noticed this issue as well: it's confusing that gitea requires one to choose a local password even when authentication is done with an external account. (The other possibility, "linking", is, if I understand correctly, to entrust one's password at the other service with the gitea instance - this seems outright dangerous!)

On our gitlab server, we regularly get contributions (e.g. issues are opened) by people who log in with their github or google account. Doing the same with gitea is too complicated and creates too much of a barrier for one-of-a-time contributors.

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Oct 3, 2018

@grothesque "linking" just means that you enable multiple styles of login. People may pick the same password, but that is not the intent.

@solderjs solderjs referenced this issue Oct 3, 2018

Merged

UX of link account (Step 1) #5006

8 of 8 tasks complete
@grothesque

This comment has been minimized.

Copy link

grothesque commented Oct 3, 2018

@coolaj86, that's reassuring, thank you for the explaination. Let me explain why I was confused.

When a new user signs in to try.gitea.io using a github account, a page shows up with the instructions: "Sign in with existing credentials to link your existing account to this account. Or register a new one." Below, two boxes are visible, one with the title "sign in", the other one "register". The first box clearly corresponds to the first option "sign in with existing credentials", and to me that meant "with existing github credentials", even more so because my github username was already filled-in!

Now, thanks to your explaination, I understand that "sign in" in the above case means: sign in with a previously created local gitea account and link it to the github account. IMHO this is not at all clear to a new user.

If you ask me, the action of linking an existing gitea account should be moved to the settings section of gitea. I also believe that the registering of a new account should be automatic with a random password, that can be changed later if necessary.

@daviian

This comment has been minimized.

Copy link
Member

daviian commented Oct 4, 2018

@grothesque
I agree with you that OAuth login should transparently create an account and perfrom the log in as requested. However IMO setting a random password is a bad habit. Instead I prefer to disable "local" sign-in and http(s) cloning since the user doesn't know the password anyway.

A naive approach to the "account linking problem" can be to look for a local user with the same email address or username and ask the user for actions in that event.
I believe a user who performs OAuth login is either a completely new user or has forgotten about his/her existing user at all.
So my suggestions would be to remind the OAuth user that he has to set a password to be able to use http(s) cloning perhaps with a horizontal banner below navigation, include a link that directs them to password setup and local account linking.

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Oct 5, 2018

@grothesque @daviian I've started work on this (baby steps, highest value lowest cost first):
#5006

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Oct 5, 2018

Progress. Here's a screencap and video of the requested changes:

https://youtu.be/jM8NL4IMM5Y

@grothesque

This comment has been minimized.

Copy link

grothesque commented Oct 5, 2018

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Oct 7, 2018

Okay, I've got something that's ready to test (I'd say the 20% of the work that provides 80% of the most important solutions)

See it in action

I've got it running on my gitea instance at https://git.coolaj86.com

If you'd like to test it out for yourself

My pull requests are against master, but I run them on my gitea as backport to v1.5.1 (includes #5006, #5029, #5033):

git clone https://github.com/coolaj86/gitea.git gitea.coolaj86 -b v1.5.1-coolaj86
pushd gitea.coolaj86
TAGS="bindata sqlite" make generate all

I would not recommend replacing your existing gitea, but rather creating a symlink so that you can easily switch back if you don't like it. For example, if you keep gitea in /opt/gitea/bin:

rsync -av ./gitea /opt/gitea/bin/gitea-v1.5.1-coolaj86
pushd /opt/gitea/bin
mv gitea gitea-v1.5.1
ln -s gitea-v1.5.1-coolaj86 gitea

I've run a couple of manual tests so far, so I feel comfortable with someone else trying it out. I won't be pushing any additional changes to that branch (such as the upcoming changes to address the empty checkboxes in the issue) until I've tested them in production for myself.

@grothesque

This comment has been minimized.

Copy link

grothesque commented Oct 8, 2018

@solderjs

This comment has been minimized.

Copy link
Contributor Author

solderjs commented Oct 8, 2018

@grothesque Good catch.

I made each PR independent from the other so that they could be accepted as small changes, without any merge conflicts, rather than a lump change.

Because of that, I updated the wording in one PR, but not in the other that also modified the same template.

I’ll see if I can find better wording that will apply to both equally well and add a commit for it.

@ix5

This comment has been minimized.

Copy link

ix5 commented Oct 23, 2018

As it currently stands, you cannot delete your account without sending a password-reset request to your backup email.
I guess this is a problem with all actions that gitea wants you to confirm with your own password. How do we solve this?

@strk

This comment has been minimized.

Copy link
Member

strk commented Oct 25, 2018

@stale

This comment has been minimized.

Copy link

stale bot commented Jan 5, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs during the next 2 weeks. Thank you for your contributions.

@stale stale bot added the stale label Jan 5, 2019

@clarfon

This comment has been minimized.

Copy link
Contributor

clarfon commented Jan 7, 2019

So, it seems like #5033 and #5029 still aren't merged, and I think those two plus what's already been merged are what's needed to finish this? Or is there something that those PRs don't cover?

@stale stale bot removed the stale label Jan 7, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment