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

Implement idP resolution using OpenID Connect Discovery #13

Open
binki opened this Issue Aug 1, 2016 · 14 comments

Comments

Projects
None yet
6 participants
@binki

binki commented Aug 1, 2016

The concept of idPs resolved via email addresses (instead of OpenID discovery URIs) is my favorite feature of Persona. It made Persona much easier to use than traditional OpenID where a website either only permits the user to select from a list of trusted OpenID providers or the user has to paste in a “discovery URI”. Persona used the authority information inherent in the user’s identity (email), the domain name, as the starting point for idP discovery. Analogous to how an MTA looks up MX records, Persona checked for well-known DNS records to see if the mail provider defined an idP. If the mail provider did provide an idP, the user would get a much nicer login experience.

Based on how letsauth is right now implementing OpenID Connect and how it seems like a standard which will get wide adoption, perhaps letsauth could set a goal to define an idP lookup mechanism similar to Persona. Or maybe OpenID Connect already defines a way to do this…

At http://openid.net/connect/ , I see Discovery and Dynamic Registration. So perhaps Discovery defines a way to find what OpenID Connect provider should be used for an email address. Right in the spec is a description of how to form a WebFinger lookup for a typed email address. Given email user@example.org, you would send a request to https://example.org/.well-known/webfinger?resource=acct%3auser%40example.org&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer. If the idP implements this optional endpoint, the request should yield enough information to find the OpenID Connect idP for the user.

Of course, this is only the first step of a many step process and there are other points where failure could happen. I think maybe letsauth would have to use Dynamic Client Registration and keep track of its registrations with providers, etc. But at a glance it appears that decentralized idP lookup based on email address is already defined by a well-accepted standard and the standard which letsauth is using as a basis for its own protocol, nonetheless.

Of course, such a mechanism can’t replace letsauth. For email addresses for which OpenID Connect providers cannot be discovered, the email hash login would provide a baseline login experience. And a letsauth relier can be kept clean and simple if it can always delegate to letsauth rather than implementing the discovery itself.

@onli

This comment has been minimized.

Show comment
Hide comment
@onli

onli Aug 2, 2016

Member

Hi @binki. I remember that we talked in an IRC meeting about this. I think that the WebFinger lookup was used in one model, maybe that was an idea of @skorokithakis at that time? Currently and as far as I could see, https://github.com/letsauth/ladaemon uses the discovery URL, see https://github.com/letsauth/ladaemon/blob/master/src/oidc.rs#L42. But I think that's where the webfinger lookup could be added.

Member

onli commented Aug 2, 2016

Hi @binki. I remember that we talked in an IRC meeting about this. I think that the WebFinger lookup was used in one model, maybe that was an idea of @skorokithakis at that time? Currently and as far as I could see, https://github.com/letsauth/ladaemon uses the discovery URL, see https://github.com/letsauth/ladaemon/blob/master/src/oidc.rs#L42. But I think that's where the webfinger lookup could be added.

@onli onli added the enhancement label Aug 2, 2016

@binki

This comment has been minimized.

Show comment
Hide comment
@binki

binki Aug 2, 2016

I ended up finding a discussion of something similar in the ML archives after the fact. Thanks for pointing to the spot in the source, maybe I’ll find time to play with the daemon, but I’m not sure yet.

binki commented Aug 2, 2016

I ended up finding a discussion of something similar in the ML archives after the fact. Thanks for pointing to the spot in the source, maybe I’ll find time to play with the daemon, but I’m not sure yet.

@binki

This comment has been minimized.

Show comment
Hide comment
@binki

binki Aug 3, 2016

So, I’ve started trying to write my own dummy idP since I don’t know off-hand of any example implementations. So far I have a faked out webfinger: https://client.webfinger.net/lookup?resource=ohnobinki%40turbo.coffee . We’ll see if I get any futher… ;-).

binki commented Aug 3, 2016

So, I’ve started trying to write my own dummy idP since I don’t know off-hand of any example implementations. So far I have a faked out webfinger: https://client.webfinger.net/lookup?resource=ohnobinki%40turbo.coffee . We’ll see if I get any futher… ;-).

@callahad

This comment has been minimized.

Show comment
Hide comment
@callahad

callahad Aug 4, 2016

Member

(Pinging @rfk to get his blog post up on OpenID Connect and the quality of its dynamic registration spec...)

Member

callahad commented Aug 4, 2016

(Pinging @rfk to get his blog post up on OpenID Connect and the quality of its dynamic registration spec...)

@callahad

This comment has been minimized.

Show comment
Hide comment
@callahad

callahad Aug 4, 2016

Member

Actually, I somehow missed the attachment on one of @rfk's responses in that mailing list thread. That's a very good place to start.

at a glance it appears that decentralized idP lookup based on email address is already defined by a well-accepted standard and the standard which letsauth is using as a basis for its own protocol, nonetheless.

That's also my understanding. Without having actually implemented it, Webfinger does indeed look reasonable for discovery of a user's identity provider... but then we have to solve the problem of our daemon being able to talk to that provider without having been previously introduced and registered.

Similarly, what protocol does the provider speak? How do we make that easy enough for individuals with their own domain to become their own identity providers?

I don't have the headspace to tackle this right now, but I'd absolutely love it if someone (@binki, et al.) began exploring in this direction. It can be cleanly added to our list of auth strategies in the future, so it's not a blocker for v1.0, but we will need to solve this problem before we can claim to have lived up to our goal of empowering people to self-certify.

Member

callahad commented Aug 4, 2016

Actually, I somehow missed the attachment on one of @rfk's responses in that mailing list thread. That's a very good place to start.

at a glance it appears that decentralized idP lookup based on email address is already defined by a well-accepted standard and the standard which letsauth is using as a basis for its own protocol, nonetheless.

That's also my understanding. Without having actually implemented it, Webfinger does indeed look reasonable for discovery of a user's identity provider... but then we have to solve the problem of our daemon being able to talk to that provider without having been previously introduced and registered.

Similarly, what protocol does the provider speak? How do we make that easy enough for individuals with their own domain to become their own identity providers?

I don't have the headspace to tackle this right now, but I'd absolutely love it if someone (@binki, et al.) began exploring in this direction. It can be cleanly added to our list of auth strategies in the future, so it's not a blocker for v1.0, but we will need to solve this problem before we can claim to have lived up to our goal of empowering people to self-certify.

@djc

This comment has been minimized.

Show comment
Hide comment
@djc

djc Aug 4, 2016

Member

So dynamic registration is one thing, but IIRC we wanted to try and keep the daemon stateless, right? And I assume most OIDC providers would not work well with trying to register the same callback URL a second time...

Member

djc commented Aug 4, 2016

So dynamic registration is one thing, but IIRC we wanted to try and keep the daemon stateless, right? And I assume most OIDC providers would not work well with trying to register the same callback URL a second time...

@callahad

This comment has been minimized.

Show comment
Hide comment
@callahad

callahad Aug 4, 2016

Member

IIRC we wanted to try and keep the daemon stateless, right?

Right. This is somewhere we likely need to develop a custom protocol, taking inspiration from Persona and OIDC Dynamic Registration and maintaining as much of OIDC as makes sense, without requiring true registration or long-term statefulness.

Member

callahad commented Aug 4, 2016

IIRC we wanted to try and keep the daemon stateless, right?

Right. This is somewhere we likely need to develop a custom protocol, taking inspiration from Persona and OIDC Dynamic Registration and maintaining as much of OIDC as makes sense, without requiring true registration or long-term statefulness.

@binki

This comment has been minimized.

Show comment
Hide comment
@binki

binki Aug 4, 2016

Right now I’m slowly working through an implementation of the idP side of the specs as I have time. Hopefully the specs have some allowance for registration-free authentication, I think someone in that ML thread mentioned that implicit flow might allow that, but I haven’t gotten that far yet myself. Avoiding registration would also make the idP itself simpler.

One thing I did want to note here is a decision I’ve made about the WebFinger lookup itself. The OpenID Connect Discovery 1.0 says:

To find the Issuer for the given user input in the form of an e-mail address joe@example.com

in this case, the acct: scheme [I‑D.ietf‑appsawg‑acct‑uri] is prepended to the Identifier.

However, the acct: spec and WebFinger spec describe acct: as intended for looking up non-email accounts. My interpretation of WebFinger’s spec is that: if you want to look up general accounts that might not have emails associated with them, use acct: (and you can end up with something unassociated with an email, like acct:ohnobinki@twitter.com). But if you want to, you can use WebFinger to resolve an email address to an account by just using mailto:email@example.org. It is up to the WebFinger implementation whether or not it supports resolving mailto: and that would just be one of many requirements Portier would have for services to implement OPTIONAL features from the specs to support Portier ;-). I.e., Portier should do the idP lookup using mailto: rather than acct: to properly respect the specs.

I’ve implemented an example of that at https://client.webfinger.net/lookup?resource=mailto%3Aohnobinki%40turbo.coffee where mailto:ohnobinki@turbo.coffee resolves to acct:ohnobinki@turbo.coffee. Yay a tiny bit of progress? I have a long way to go… xD

binki commented Aug 4, 2016

Right now I’m slowly working through an implementation of the idP side of the specs as I have time. Hopefully the specs have some allowance for registration-free authentication, I think someone in that ML thread mentioned that implicit flow might allow that, but I haven’t gotten that far yet myself. Avoiding registration would also make the idP itself simpler.

One thing I did want to note here is a decision I’ve made about the WebFinger lookup itself. The OpenID Connect Discovery 1.0 says:

To find the Issuer for the given user input in the form of an e-mail address joe@example.com

in this case, the acct: scheme [I‑D.ietf‑appsawg‑acct‑uri] is prepended to the Identifier.

However, the acct: spec and WebFinger spec describe acct: as intended for looking up non-email accounts. My interpretation of WebFinger’s spec is that: if you want to look up general accounts that might not have emails associated with them, use acct: (and you can end up with something unassociated with an email, like acct:ohnobinki@twitter.com). But if you want to, you can use WebFinger to resolve an email address to an account by just using mailto:email@example.org. It is up to the WebFinger implementation whether or not it supports resolving mailto: and that would just be one of many requirements Portier would have for services to implement OPTIONAL features from the specs to support Portier ;-). I.e., Portier should do the idP lookup using mailto: rather than acct: to properly respect the specs.

I’ve implemented an example of that at https://client.webfinger.net/lookup?resource=mailto%3Aohnobinki%40turbo.coffee where mailto:ohnobinki@turbo.coffee resolves to acct:ohnobinki@turbo.coffee. Yay a tiny bit of progress? I have a long way to go… xD

@rfk

This comment has been minimized.

Show comment
Hide comment
@rfk

rfk Aug 4, 2016

Hopefully the specs have some allowance for registration-free authentication, I think
someone in that ML thread mentioned that implicit flow might allow that

Sadly, I'm pretty sure the existing implicit flow does not allow this, mostly because it would be a security problem - you need some way to tell what redirect_uri values are legit for each client.

I think we could invent a protocol that securely replaces "client registration" with "client discovery" along the lines of [1], but I'm not aware of any existing spec that avoids pre-registration of client credentials.

we wanted to try and keep the daemon stateless, right? And I assume most
OIDC providers would not work well with trying to register the same callback URL a second time

If you're happy to keep a bit of state in a cache to reduce overall server load, you might actually get away with this. IIUC the dynamic registration spec is fine with you registering the same client details multiple times, and re-registering is the recommended way to e.g. replace your client secret in the event of a data breach.

[1] https://groups.google.com/d/msg/portier/kOc_SMY_9lI/DIuoD2HUBwAJ

rfk commented Aug 4, 2016

Hopefully the specs have some allowance for registration-free authentication, I think
someone in that ML thread mentioned that implicit flow might allow that

Sadly, I'm pretty sure the existing implicit flow does not allow this, mostly because it would be a security problem - you need some way to tell what redirect_uri values are legit for each client.

I think we could invent a protocol that securely replaces "client registration" with "client discovery" along the lines of [1], but I'm not aware of any existing spec that avoids pre-registration of client credentials.

we wanted to try and keep the daemon stateless, right? And I assume most
OIDC providers would not work well with trying to register the same callback URL a second time

If you're happy to keep a bit of state in a cache to reduce overall server load, you might actually get away with this. IIUC the dynamic registration spec is fine with you registering the same client details multiple times, and re-registering is the recommended way to e.g. replace your client secret in the event of a data breach.

[1] https://groups.google.com/d/msg/portier/kOc_SMY_9lI/DIuoD2HUBwAJ

@binki

This comment has been minimized.

Show comment
Hide comment
@binki

binki Aug 4, 2016

Even for the email link login to work you need state, right? Since it sounds like reregistering shouldn't be too big of a deal, caching the registration token for a few minutes at a time for the sake of staying with existing standards sounds like the way to go.

binki commented Aug 4, 2016

Even for the email link login to work you need state, right? Since it sounds like reregistering shouldn't be too big of a deal, caching the registration token for a few minutes at a time for the sake of staying with existing standards sounds like the way to go.

@rfk

This comment has been minimized.

Show comment
Hide comment
@rfk

rfk Aug 4, 2016

Although TBH I'm not aware of any major OIDC IdP that actually implements the dynamic registration spec, so it may be a moot point.

rfk commented Aug 4, 2016

Although TBH I'm not aware of any major OIDC IdP that actually implements the dynamic registration spec, so it may be a moot point.

@skorokithakis

This comment has been minimized.

Show comment
Hide comment
@skorokithakis

skorokithakis Aug 4, 2016

Member

@binki You don't need state for the email login link, you can sign it using a secret so you just validate the signature when it comes back.

Member

skorokithakis commented Aug 4, 2016

@binki You don't need state for the email login link, you can sign it using a secret so you just validate the signature when it comes back.

@onli

This comment has been minimized.

Show comment
Hide comment
@onli

onli Aug 5, 2016

Member

The ladaemon however uses redis to store some state: https://github.com/portier/ladaemon/blob/master/src/store.rs. I think that's okay and would also be okay here. There is a difference between having some internal state used for making the authentication work and having state in the sense of users storing accounts and profil information. The line is where one ladeamon instance can't be swapped out with another without loosing stuff.

Member

onli commented Aug 5, 2016

The ladaemon however uses redis to store some state: https://github.com/portier/ladaemon/blob/master/src/store.rs. I think that's okay and would also be okay here. There is a difference between having some internal state used for making the authentication work and having state in the sense of users storing accounts and profil information. The line is where one ladeamon instance can't be swapped out with another without loosing stuff.

@djc

This comment has been minimized.

Show comment
Hide comment
@djc

djc Aug 5, 2016

Member

Yeah, ladaemon has some state, but it's all short-lived. So if the database breaks down or whatever (or, if you want to setup your own instance), nothing is lost other than some limited performance penalty due to having to fetch more data in order to do the job.

Member

djc commented Aug 5, 2016

Yeah, ladaemon has some state, but it's all short-lived. So if the database breaks down or whatever (or, if you want to setup your own instance), nothing is lost other than some limited performance penalty due to having to fetch more data in order to do the job.

@3n-mb 3n-mb referenced this issue Aug 7, 2016

Closed

Where are the specs? #1

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