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

Describing auth for content resources rather than Image API services #547

Closed
tomcrane opened this issue Aug 23, 2015 · 16 comments
Closed

Describing auth for content resources rather than Image API services #547

tomcrane opened this issue Aug 23, 2015 · 16 comments

Comments

@tomcrane
Copy link
Contributor

(this is a prezi issue, not an Image API issue):

In a manifest:

{
  "@id":"http://www.example.org/iiif/book1/res/page1.jpg",
  "@type":"dctypes:Image",
  "format":"image/jpeg",
  "service": {
    "@context": "http://iiif.io/api/image/2/context.json",
    "@id":"http://www.example.org/images/book1-page1",
    "profile":"http://iiif.io/api/image/2/profiles/level2.json",
    "service" : {
      "@id": "https://authentication.example.org/login",
      "profile": "http://iiif.io/api/auth/0/login",
      // ... etc
    }
  }
}

A resource like this can appear in a manifest either as the body of an annotation on a canvas, or as a thumbnail. This one has an image API service, which in turn has a login service. So we know that if we want pixels from the service we need to use its login service as per the image API. That's now covered by the auth spec.

As the manifest publisher, I want to assert that http://www.example.org/iiif/book1/res/page1.jpg also requires auth. It's the @id of the image resource. How do I do that?

I have some real use cases:

  1. The resource is the image anno on a canvas and its @id points to a 1000px JPEG for manifest consumers that don't do the image API. This 1000px image must be protected. In my case, if you acquire a token by the login service mentioned in the resource's Image API service it will also let you see the 1000px static JPEG, but that should not be a universal assumption.

  2. The resource is a thumbnail property of a canvas. In other respects it is the same as 1) - the @id of the resource links to a 150px thumbnail image, and the resource also has a level 0 Image API service giving an array of three pre-canned thumbnails, which still require auth. I'm putting these details in the manifest for fast thumbnail retrieval (client can construct all thumbnail URLs from the manifest alone)

  3. The resource is a small thumbnail on a canvas - it is the same as 2) except that the @id URL does not require auth, but the image resource has a service giving access to three larger thumbnails (a sizes array on the level0 Image API service) that do require auth. Archive material is allowed thumbs up to 150px with auth required for anything larger.

While the auth spec isn't confined to Image API service resources (it works for canvases, sequences etc), can we use it here? The resource URL doesn't resolve to a JSON-LD resource like an info.json or a manifest, it gives you an actual "image/jpeg". If the login service is asserted on the resource itself, as well as the resource's Image API service, should the consumer then start requesting the JPEG with XHR to look at the HTTP status code? The @id value would usually just be set as the src of an img tag rather than processed like an info.json.

In a manifest:

{
  "@id":"http://www.example.org/iiif/book1/res/page1.jpg",
  "@type":"dctypes:Image",
  "format":"image/jpeg",
  "service": [
    {  
      // the image API...
      "@context": "http://iiif.io/api/image/2/context.json",
      "@id":"http://www.example.org/images/book1-page1",
      "profile":"http://iiif.io/api/image/2/profiles/level2.json",
      "service" : {
        // a log in for getting pixels from image API
        "@id": "https://authentication.example.org/login",
        "profile": "http://iiif.io/api/auth/0/login",
        // ... etc
      }
    },
    {   
      // a log in for page1.jpg
      "@id": "https://authentication.example.org/login",
      "profile": "http://iiif.io/api/auth/0/login",
      // ... etc
    }
  ]
}

Here the same login service is asserted on the Image API service and the static image resource http://www.example.org/iiif/book1/res/page1.jpg. A client that had first encountered the login service @id https://authentication.example.org/login as a service on an info.json and had stored the token would know the user is authorised for this image too - but a client that isn't storing this, or sees it on an image first, would be expected to start interacting with the resource on which the service is asserted as per the auth spec - but the resource is a JPEG, not a JSON-LD document.

Is this the way to do it?

@azaroth42
Copy link
Member

Yes to having service associated with the static image to assert where you can authenticate.
Whether it's a auth/0/login sort of service, I'm less certain but it seems feasible. The service should set a cookie ... which could also be used to get access to the image.

@tomcrane
Copy link
Contributor Author

Two ways of doing this (thumbnails omitted to reduce noise)

  1. http://tomcrane.github.io/iiifauth/scratch-manifests/auth-in-manifest-first-serialised.json

The login services are asserted on both the image resource and the image resource's service. In this manifest I'm using an optimisation briefly discussed in Princeton - the first time the login service block is serialised it is serialised in full, and thereafter it is referred to just by the URI, having already been asserted in the graph.

The trouble with this, especially as login services can appear on multiple resource types, is that it puts a lot of work on the viewer to find and keep track of the services used, especially as it likely won't be using an RDF engine to process the data. Here for instance the full service info does not appear on any Image API service because the resource version happens to be asserted first. And if I switched on my thumbnails that would be the place where the serialiser asserts the full service.

It would be better if the manifest could provide the services once in a consistent place, and clients could assume that auth services asserted on resources will always be URIs only. That is, the manifest comes with a list of all the auth services it thinks are referenced.

hence:
2) http://tomcrane.github.io/iiifauth/scratch-manifests/auth-in-manifest-explicit.json

...which is easier to read and process (especially for the first canvas), but introduces another problem. I've asserted the services on the manifest - but the services don't really apply to the manifest resource itself (which happens to be always open in this case). It means the image resources consistently look like this:

"resource": {
  "@id": "http://local.wellcomelibrary.org/iiif-img/b18409106-0/84ea979c-2598-4b51-9f42-215b64f3497e/full/!800,800/0/default.jpg",
  "@type": "dcTypes:Image",
  "format": "image/jpeg",
  "height": 800,
  "width": 577,
  "service": [
    {
      "@context": "http://iiif.io/api/image/2/context.json",
      "@id": "http://local.wellcomelibrary.org/iiif-img/b18409106-0/84ea979c-2598-4b51-9f42-215b64f3497e",
      "profile": "http://iiif.io/api/image/2/level1.json",
      "service": [
        "http://local.wellcomelibrary.org/iiif/accepttermslogin",
        "http://local.wellcomelibrary.org/service/login/clickthrough"
      ]
    },
    "http://local.wellcomelibrary.org/iiif/accepttermslogin",
    "http://local.wellcomelibrary.org/service/login/clickthrough"
  ]
}

...which is cleaner.

Do we need some extra info to convey that these services don't apply to the manifest resource itself? Or assert them differently (but still at the manifest root)?

@tomcrane
Copy link
Contributor Author

Some more thinking on applying the auth client and server flows to image resources and other binaries (i.e., not services).

https://gist.github.com/tomcrane/e12f81f76d4f4348a301

Condensed version client:

Use the standard client auth flow with slight changes for binaries

  • If the resource protected by the auth service is a recognised service (identified by profile) that you know will return an info.json descriptor, make a GET request as per current auth flow, so that you can process the body (and detect auth services, which override any that we may have declared in the manifest for optimisation purposes)
  • If it's anything else (an image, a video, a PDF), expect to find auth services asserted on the binary resource in the manifest. Make a HEAD request instead. You can't do anything with the response body so don't ask for it.

Condensed version server:

  • Make sure you handle HEAD requests for binaries properly. Return 200, 401, 403 as appropriate. If you serve manifests, assert the auth services on binaries there. Not sure what to do about redirects.

Flow:

  • viewer sees auth service for mp3 declared in manifest

Initial HEAD:

$ curl -I http://local.wellcomelibrary.org/media/c973c568.mp3
HTTP/1.1 401 Unauthorised
  • viewer presents auth UI flow
  • window closes
  • acquires token
  • send token:

CORS preflight:

$ curl -X OPTIONS -i http://local.wellcomelibrary.org/media/c973c568.mp3
HTTP/1.1 200 OK
Content-Length: 0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: HEAD, GET, OPTIONS
Access-Control-Allow-Headers: Authorization

then:

$ curl -I -H "Authorization: Bearer my_token" http://local.wellcomelibrary.org/media/c973c568.mp3
HTTP/1.1 200 OK
Content-Length: 0
  • we're good, send this mp3 to your audio playing widget or html5 tag

This flow works for an image resource in the same way.

@azaroth42
Copy link
Member

In this manifest I'm using an optimisation briefly discussed in Princeton - the first time the login service block is serialised it is serialised in full, and thereafter it is referred to just by the URI, having already been asserted in the graph.

Is this the solution to #526? If we're agreed (let's discuss there) I'm happy to write it up in Prezi 2.1

@azaroth42
Copy link
Member

I'm less happy about option 2, where the services are separated from the resources that they're services for. In the case where there are services randomly scattered around (e.g. not every image requires authentication), then the cost of repeating the information is low.

I think the logic could be:

Look at the images on the first canvas, then the start canvas (if present and different) and then the second canvas. If the login isn't on any of those, then repeat it everywhere needed?

@zimeon
Copy link
Member

zimeon commented Dec 2, 2015

Agreement in eds meeting that this is not in scope for prezi2.1

@azaroth42
Copy link
Member

Confirmed defer, but important :)

@azaroth42
Copy link
Member

Confirmation on 8/8/17 AV call: This is needed, though no further suggestions as to alternative or better approaches to the ones already proposed

@jwd
Copy link

jwd commented Aug 8, 2017

For more detail on auth as related to AV, see notes from Feb 2017 British Library meeting

@azaroth42
Copy link
Member

Good to discuss in person in LA / Toronto, and bring results back to calls (and the issue)

@azaroth42 azaroth42 changed the title Describing auth for image resources rather than Image API services Describing auth for content resources rather than Image API services Sep 15, 2017
@azaroth42 azaroth42 modified the milestones: Presentation 2.2, Presentation 3.0 Sep 20, 2017
@azaroth42
Copy link
Member

Document the need for services.json as the layer for the client to determine whether or not the user is authorized or not, as per the responses for info.json in the Image API.
E.g. in test fixture: https://raw.githubusercontent.com/IIIF/iiif-av/master/source/api/av/examples/14.json

Need a profile for the auth services wrapper that will act as the probe.

@azaroth42 azaroth42 added A/V and removed discuss labels Oct 2, 2017
@azaroth42
Copy link
Member

Toronto eds: The services document should act as a probe when there's no info.json, but it's just a description of services. It seems very wrong to have it respond with 401 when the user is not authed, but there are other services unrelated to auth.

Proposal: Create a new "probe" service explicitly for Auth, that you do a HEAD on, and the only thing that matters is the HTTP Status code. This would let the probe service be the content resource if it does the right thing for HEAD, and a separate little service that does the right thing if the content resource is harder to control the implementation to that level of detail (e.g. in a legacy badly written DAMS)

@azaroth42
Copy link
Member

Tag @workergnome -- thoughts on above proposal, w.r.t. your concerns with the previously described approach?

@workergnome
Copy link

The idea would be to have a predicate that indicates that this is the resource that you hit to get the HTTP status response from a HEAD request?

I like that quite a lot.

@azaroth42
Copy link
Member

Hrm, now that you say that, and I think in terms of RDF ... I guess it would need to be a predicate, not just using it as the id of a different Service, to avoid polluting the description of the content resource.

@jpstroop
Copy link
Member

Not ratified at WG meeting in Toronto due to discussion of probe service. Split into two issues: #1289, #1290. Closing in favor of those.

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

6 participants