Page MenuHomePhabricator

OpenID Authentication as an Auth Option
Closed, WontfixPublic

Description

@phuzion requested OpenID auth, and I see no reason not to support it.

@epriestley said it looks like we could start with this http://gitorious.org/lightopenid/lightopenid/blobs/master/openid.php

No ETA on this, but I think it'd be cool.

Details

Differential Revisions
Restricted Differential Revision

Event Timeline

codeblock triaged this task as Low priority.
codeblock added a project: Phabricator.
codeblock added subscribers: codeblock, phuzion, epriestley.
hvaara added a subscriber: hvaara.Apr 30 2012, 8:48 AM
codeblock edited this Maniphest Task.Aug 1 2012, 8:45 PM
codeblock edited this Maniphest Task.Jan 4 2013, 1:09 PM
codeblock lowered the priority of this task from Low to Wishlist.Jan 4 2013, 1:10 PM
codeblock removed codeblock as the assignee of this task.Jan 20 2013, 12:24 AM

Testing something

Done testing something

epriestley closed this task as Wontfix.Jun 20 2013, 7:50 PM

This is now practical, and I built half a version based on D3122, but I'm somewhat disinclined to pursue it further in the upstream. In particular:

  • OpenID is fairly complicated from a server-side point of view. We realistically need a library for it, and although LightOpenID is only about 800 lines long, it has many weird special cases. If there's adoption, I'm worried there will be a significant support cost associated with OpenID, akin to the relatively high support cost we currently bear for LDAP. I don't really want to learn the ins and outs of OpenID in order to support it.
  • Similarly, a lot of what can go wrong with OpenID is very opaque and not necessarily reproducible. I'm worried it will be difficult to debug and support, adding to the cost of all the special casing. If a user tries to log into another provider (other than LDAP) and hits an error I can usually create a similar situation fairly easily, but this might be hard with OpenID.
  • There hasn't been much demand for OpenID, and the future of OpenID is unclear. Of the providers here, we support Google via OAuth already and could add Yahoo oauth if anyone ever asked for it; the others are mostly either Yahoo properties or essentially unknown/irrelevant.
  • This graph doesn't look too hot: {F46992}
  • Is anyone still backing OpenID? Mozilla seems to be throwing its weight behind Persona, and I feel like everyone else is on OAuth? I haven't seen any new providers announce OpenID recently, and all the tools in this space seem to provide OAuth instead (GitHub, Bitbucket).

I'm not necessarily totally opposed to doing this at some point, but interest seems very low and cost relatively high. This can be built as an extension today, so maybe that's the best route forward? Here's my half-implementation:

{P865}

{P866}

If you dump all that into a library and have Phabricator load it, it will get you sort of half-ish way there.

d2722 is great.

D2722 is an object.

I referenced this recently in conjunction with a request for SAML auth, since many of the same arguments against OpenID apply to SAML. While it's on my mind, two other notes:

  1. We possibly dodged a security bullet by not implementing this, see https://www.facebook.com/BugBounty/posts/778897822124446 (although LightOpenID parses XML with regular expressions and wouldn't have been vulnerable with that specific implementation).
  2. Basecamp implemented (in Jun, 2007) and then removed (in May, 2011) OpenID support from their product: "The only feature I can think of that we ever removed (I might be wrong) is OpenID, because almost no-one used it and it was really confusing." (see https://news.ycombinator.com/item?id=7183031).
epriestley changed the visibility from "All Users" to "Public (No Login Required)".May 15 2015, 9:52 PM

Would you considered taking prioritized funding for implementing OpenID?

I'm not sure how I'd even test OpenID these days. Offhand, I haven't seen any provider offering OpenID in years.

OpenID seems to have been supplanted by "OpenID Connect", which is (as far as I can tell) just OAuth 2.0 with a prescribed API structure that has no technical relationship to the original OpenID protocol. So that's perhaps a tentative "maybe" -- e.g., I implemented Okta OAuth in T13394, which is also known as "Okta OpenID Connect", but this is completely different from what "OpenID" meant at the time this task was filed.

If you want to auth against some internal OpenID service which is real OpenID and has been running since 2010, I probably have no realistic way to test that code actually works.

Apologies, I should have provided more context. We've been futzing with this project which OIDC. We'd be looking to ensure that it works with Dex.

epriestley added a comment.EditedFri, Sep 27, 1:54 AM

OIDC is just OAuth2 in a ghost costume, so the implementation is nearly trivial.

If you have a staging environment, drop these two files into phabricator/src/extensions/:

PhabricatorDexAuthProvider.php
<?php

final class PhabricatorDexAuthProvider
  extends PhabricatorOAuth2AuthProvider {

  public function getProviderName() {
    return pht('Dex');
  }

  protected function getProviderConfigurationHelp() {
    return PhabricatorEnv::getURI($this->getLoginURI());
  }

  protected function newOAuthAdapter() {
    return new PhutilDexAuthAdapter();
  }

  protected function getLoginIcon() {
    return 'Dex';
  }

}
PhutilDexAuthAdapter.php
<?php

final class PhutilDexAuthAdapter
  extends PhutilOAuthAuthAdapter {

  private $wellKnownConfiguration;

  public function getAdapterType() {
    return 'dex';
  }

  public function getDexBaseURI() {
    return new PhutilURI('http://127.0.0.1:5556/dex');
  }

  public function getAdapterDomain() {
    return $this->getDexBaseURI()->getDomain();
  }

  public function getAccountID() {
    return $this->getOAuthAccountData('sub');
  }

  public function getAccountEmail() {
    return $this->getOAuthAccountData('email');
  }

  public function getAccountName() {
    return null;
  }

  public function getAccountImageURI() {
    return null;
  }

  public function getAccountURI() {
    return null;
  }

  public function getAccountRealName() {
    return $this->getOAuthAccountData('name');
  }

  protected function getAuthenticateBaseURI() {
    return $this->getWellKnownConfiguration('authorization_endpoint');
  }

  protected function getTokenBaseURI() {
    return $this->getWellKnownConfiguration('token_endpoint');
  }

  public function getScope() {
    return 'openid profile email';
  }

  public function getExtraAuthenticateParameters() {
    return array(
      'response_type' => 'code',
    );
  }

  public function getExtraTokenParameters() {
    return array(
      'grant_type' => 'authorization_code',
    );
  }

  public function getAccessToken() {
    return $this->getAccessTokenData('access_token');
  }

  protected function loadOAuthAccountData() {
    $uri = $this->getWellKnownConfiguration('userinfo_endpoint');

    $future = new HTTPSFuture($uri);

    $token = $this->getAccessToken();
    $future->addHeader('Authorization', "Bearer {$token}");

    list($body) = $future->resolvex();

    try {
      $result = phutil_json_decode($body);
      return $result;
    } catch (PhutilJSONParserException $ex) {
      throw new PhutilProxyException(
        pht('Expected valid JSON response from OIDC account data request.'),
        $ex);
    }
  }

  private function getWellKnownConfiguration($key) {
    if ($this->wellKnownConfiguration === null) {
      $uri = $this->getDexBaseURI();

      $path = $uri->getPath();
      $path = rtrim($path, '/').'/.well-known/openid-configuration';

      $uri->setPath($path);

      $uri = phutil_string_cast($uri);

      $future = new HTTPSFuture($uri);
      list($body) = $future->resolvex();

      $data = phutil_json_decode($body);

      $this->wellKnownConfiguration = $data;
    }

    if (!isset($this->wellKnownConfiguration[$key])) {
      throw new Exception(
        pht(
          'Expected key "%s" in well-known configuration!',
          $key));
    }

    return $this->wellKnownConfiguration[$key];
  }

}

In the second file, modify getDexBaseURI() to point at your Dex server.

Pick a client ID and secret and create a corresponding adapter in AuthLogin and Registration. It should look like this. Note the circled callback URL:

Dex should have the same config, here's what my modified config-dev.yaml file looks like:

That was good enough to get things working for me:

If it works for you, I'll polish these up and upstream them, although the final versions may look slightly different since I'd like to chip away at T6703 before bringing more providers upstream.