Skip to content
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

Transmitting a hashed passphrase in OPDS and in an OAuth flow #41

Open
HadrienGardeur opened this issue Nov 13, 2019 · 9 comments
Open

Comments

@HadrienGardeur
Copy link
Member

In order to provide a seamless experience with LCP, a number of organizations are interested in the ability to transmit the hashed passphrase associated to a user/license.

This has been implemented before by:

  • NYPL in OPDS, strictly for ACS currently
  • TEA/Vivlio using an OAuth Response Document with the Resource Owner Password Credentials grant

IMO, we need to cover the following scenarios:

  • discovery though an OPDS 1.x entry
  • discovery though an OPDS 2.0 publication
  • discovery through an OAuth Response Document (for multiple grant types)

OPDS 2.0 and OAuth could use the same JSON key, while OPDS 1.x requires an XML element and a namespace for it.

cc @leonardr @llemeurfr @RemiBauzac @picheromy

@HadrienGardeur
Copy link
Member Author

Here's my proposal:

  • let's define a new JSON key for all JSON-based use cases
  • we can re-use the work done by @leonardr in XML for OPDS 1.x

Regarding the name of this new JSON key, I would suggest using lcp_hashed_passphrase for a few reasons:

  • we can't transmit the passphrase in plain text (that's forbidden by the LCP spec)
  • we can't really call it "User Key" either, since that's only true for the Basic Profile of LCP

In OAuth 2.0 this could be transmitted in the Response Document:

{
  "access_token": "2YotnFZFEjr1zCsicMWpAA",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
  "lcp_hashed_passphrase": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}

In OPDS 2.0 this could be transmitted in the properties of a link:

{
  "links": [
    {
      "href": "https://example.com/license.lcpl",
      "rel": "http://opds-spec.org/acquisition/",
      "properties":  {
        "lcp_hashed_passphrase": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
      }
    }
  ]
}

In the future, we'll be able to use this same JSON key in a user's profile as well.

@llemeurfr
Copy link
Contributor

I totally agree about using the term "LCP hashed passphrase". The LCP spec itself will be enhanced this way.

@HadrienGardeur
Copy link
Member Author

This current proposal doesn't cover use cases where the acquisition of an LCPL license triggers the discovery/exchange of a hashed passphrase through authentication.

Let me explain some of the reasons behind this:

  • we want to avoid situations where authentication replaces the normal behavior (asking the user for a passphrase), since this could be used for bad reasons (for example, using authentication to avoid giving the passphrase to the user)
  • there's a lot of complexity around authentication, which would require additional discovery for OAuth 2.0 as well as other authentication techniques

@leonardr
Copy link

4.2 of the LCP spec says "There are no requirements for how [the User Passphrase] is to be created." 4.1 talks about prompting the user, but it doesn't look like a requirement.

I bring this up because I think we (Library Simplified) do want to have a piece of middleware generate the User Passphrase for an authenticated user and then share the User Key with the user's client so they can decrypt books.

If this is a problem I'd like to talk about possible alternatives (having the client generate the User Passphrase) or safeguards (requiring that the client display the User Key in its UI somewhere).

@HadrienGardeur
Copy link
Member Author

@leonardr

It's perfectly fine to generate a passphrase as long as it's easily available to the user. You can for example provide the passphrase in the required "hint" URL included in every LCP license.

That said in general, I would recommend doing things in the following order:

  • if you have a passphrase that is well-known to the user (for example their email or birthdate), use that as your first option
  • if not, fallback to a passphrase that the user might not immediately know, but can find somehow (for example a barcode on a library card)
  • if you can't use a passphrase that a user know or can easily find, fallback to a generated passphrase (per user, not per book) that a user will be able to discover somehow (send an email or display the passphrase in the hint URL)

@RemiBauzac
Copy link

Hello @HadrienGardeur @llemeurfr and @leonardr , I'm pleased to see that the subject comes back on the top of the stack.
I'm sorry because my English is no better than it was a few months ago 😄

From Hadrien:

  • we want to avoid situations where authentication replaces the normal behavior (asking the user for a passphrase), since this could be used for bad reasons (for example, using authentication to avoid giving the passphrase to the user)
  • there's a lot of complexity around authentication, which would require additional discovery for OAuth 2.0 as well as other authentication techniques

I do not completely agree with Hadrien on the subject of authentication because:

  1. User authentication may be imposed by some suppliers/publishers (UK ones) on their content, and in this case we have found no better than :
    • generated a passphrase unknown by the user
    • to request user credentials, and thus distribute the key
  2. requesting user authentication is the simplest, most efficient and transparent way for the user. In this case (and if publishers allow it, this is the case in most countries), we have:
    • a passphrase known to the user (chosen by him or not). He can know his passphrase by seeing it on his invoice or in his personal account (from his bookshop or public library).
    • A simple way to do without the passphrase if the reading device allows hashed key retrieval
    • Another way to enter directly the passphrase on reading device.
  3. Authentication mechanisms must reflect real use cases. We do not use OAuth2 or ODPS for our own pleasure. It just answer real cases questions. Some cases are very simple, and simple things can answer it. Some cases are more complex, and complex authentications scheme are needed.

Again, getting hashed key from user authentication result (or whatever you want) must not break the "basic case". This is a device decision :

  • If the device supports hashed key retrieval and this way is available in the license --> let's go
  • If the user authentication failed or network not available or ... --> ask passphrase to the user
  • If the device doesn't support hashed key retrieval --> ask passphrase to the user.
  • If the device support hashed key retrieval but no informations in the license --> ask passphrase to the user.

So I think we can start to define what are the different key discovery use cases, and then define the best ways to answer this use cases.

I hope to have the opportunity to discuss the subject directly with you.

@HadrienGardeur
Copy link
Member Author

  1. User authentication may be imposed by some suppliers/publishers (UK ones) on their content, and in this case we have found no better than :
    • generated a passphrase unknown by the user
    • to request user credentials, and thus distribute the key

I understand that scenario, but IMO if you go down that road you're no longer part of the LCP ecosystem. It's a different DRM solution based on LCP, but that shouldn't reference LCP in its name.

  1. requesting user authentication is the simplest, most efficient and transparent way [...]
    [...] Some cases are very simple, and simple things can answer it. Some cases are more complex, and complex authentications scheme are needed.

These two statements somehow contradict one another. My second point was that authentication is a complex matter, with many scenarios that won't always result in a simple UX.

I don't think that we should be producing a technical note that will encompass all scenarios (it's impossible), which is why the suggested approach (define a JSON key and highlight how it may be used in an OAuth Response Document or in OPDS 2.0) IMO works best than what was discussed within EDRLab before.

@llemeurfr
Copy link
Contributor

llemeurfr commented Nov 23, 2019

We should not mix in the same issue discussions about the new LCP key retrieval feature we want to propose to implementers with a discussion about the core LCP passphrase access mechanism, i.e. asking the user. It is true that implementations hiding passphrases from their user cannot market their solution as Readium LCP compliant, as expressed in the LCP Compliance Rules released by EDRLab.

@HadrienGardeur, if I understand well, your proposal tries to simplify the protocol and keep only 2 steps: a. authentication (whatever it is) b. direct retrieval of the hashed passphrase. In some cases these two steps are even merged, as the hashed passphrase is given as a response to the OAuth 2.0 authentication. My first question is, what happens if an off-the-shelves OAuth server cannot easily return the hashed passphrase? (I'll take a look at https://github.com/go-oauth2/oauth2)

@RemiBauzac
Copy link

Hi @HadrienGardeur

I understand that scenario, but IMO if you go down that road you're no longer part of the LCP ecosystem. It's a different DRM solution based on LCP, but that shouldn't reference LCP in its name.

Yes you're right, I totally agree, but if I am a "book shop" (or public library) having "LCP friendly" and "not LCP friendly catalog"... what can I do ? Using Adobe (👏 ) !
I know that there is no solution in that case, this is a real issue for me.

Hi @llemeurfr,
Yes, I mix two different things. My bad ! Sorry.
I agree with the 2 steps (Authentication, then key retreival) scenario, even in OAuth2, because sometimes, the server which owns the users for authentication are not the same than the server which owns the keys. This is a little bit more complex, but it can be useful. For off-the-shelves OAuth servers, It's not really a big deal to add informations in token response.

Have a nice week end.

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

No branches or pull requests

4 participants