Estado de este Documento
Esta Versión: 1.0.0
Última Versión Estable: 1.0.0
Versión Anterior: 0.9.4
Editores:
- Michael Appleby , Universidad de Yale
- Tom Crane , Digirati
- Robert Sanderson , J. Paul Getty Trust
- Jon Stroop , Biblioteca de la Universidad de Princeton
- Simeon Warner , Universidad de Cornell
Copyright © 2015-2017 Editores y colaboradores. Publicado por IIIF Consortium bajo la licencia CC-BY, ver aviso legal.
Tabla de Contendidos
- 1. Introducción
- 2. Servicios de Autenticación
- 3. Interacción con Recursos de Acceso Controlado
- 4. Flujo de trabajo desde la Perspectiva del Cliente Navegador
- Apéndices
1. Introducción
Las especificaciones de IIIF (pronunciado “Triple-I-Efe”) son diseñadas para soportar el acceso uniforme y enriquecido a recursos alojados en todo el mundo. Es conveniente el acceso abierto al contenido, pero políticas internas, regulaciones legales, modelos empresariales, y otras limitaciones pueden requerir que los usuarios se autentiquen y sean autorizados a interactuar con algunos recursos. Los procesos de autenticación incluyen desde sencillas restricciones por dirección IP o acuerdos click-through, hasta planes de múltiples factores con proveedores de identidad segura.
Los proveedores de contenido que necesiten limitar el acceso a sus recursos pueden ofrecer acceso por niveles a versiones alternativas en lugar de una simple proposición todo-o-nada. Las versiones alternativas pueden estar degradadas en cuanto a resolución, marcas de agua, o compresión, pero son mejores que un acceso denegado.
Ofrecer acceso interoperable a contenido restringido mediante aplicaciones cliente basadas en un navegador web presenta muchos retos:
- Un único manifest de la API de Presentación de IIIF puede referenciar recursos de contenido de múltiples instituciones y, por tanto, de múltiples dominios.
- Cada versión de un recurso debe poseer una URI única para evitar que las cachés web retornen la versión incorrecta.
- Los sistemas de control de acceso existentes en las instituciones son diferentes.
- La mayoría de los visores IIIF son aplicaciones de JavaScript del lado del cliente que pueden ser servidas desde un dominio distinto de, y, por tanto, no confiable para, los servicios de imagen que se deben cargar.
- Similarmente, el dominio de los servicios de autenticación puede ser diferente del dominio de un visor o del contenido basado en IIIF. Por tanto, el servidor de autorización no debe requerir conocimiento previo del dominio que aloja el visor.
Adicionalmente, la comunidad IIIF tiene los siguientes objetivos para esta especificación:
- Un cliente IIIF no debiera autenticar al usuario; el servidor que aloja el contenido debe ser responsable de obtener las credenciales del usuario, y el visor IIIF no requiere conocimiento de, o acceso a, ese intercambio.
- Un cliente IIIF basado en navegador debe ser capaz de mantener su estado interno durante un flujo de autenticación.
- No se debiera requerir un registro de dominios confiables; cualquiera debiera poder crear cualquier tipo de visor y ejecutarlo desde cualquier parte.
- Las instituciones debieran poder usar sus sistemas de autenticación existentes sin modificarlos.
Para vencer esos retos y objetivos, la especificación de Autenticación de IIIF describe un conjunto de flujos de trabajo para guiar al usuario a través de un sistema de control de acceso existente. El proceso de autenticación del usuario está, en su mayor parte, fuera de los objetivos de esta especificación, y puede involucrar un viaje redondo a un servidor CAS, o un proveedor OAuth2, o un sistema de inicio de sesión personalizado. En este sentido, la Autenticación de IIIF no es igual a un protocolo como CAS; es un patrón para interactuar con protocolos de terceros arbitrarios.
La Autenticación de IIIF ofrece un enlace
a una interfaz de usuario para iniciar sesión, y servicios
que proporcionan credenciales, modelados como los
elementos del flujo de trabajo OAuth2. La combinación
actúa como puente hacia el sistema de control de acceso
que utiliza el servidor, sin que el cliente necesite
conocer el mismo.
En resumen, la especificación describe como:
- Desde un visor, iniciar una interacción con un sistema de control de acceso que permita al usuario adquirir las credenciales requeridas para ver contenido restringido.
- Proporcionar al cliente suficiente conocimiento del estado del usuario respecto al proveedor de contenido para garantizar una buena experiencia de usuario.
Por favor, envíe sus comentarios a iiif-discuss@googlegroups.com.
1.1. Terminología
Esta especificación distingue Recursos de
Contenido, por ejemplo, imágenes o videos, y Recursos
de Descripción que cumplen las especificaciones
de IIIF, por ejemplo, recursos de
información de imagen de la API de Imagen
(info.json) y recursos collection o manifest de la API de
Presentación. Desde el punto de vista de una
aplicación basada en navegador, los Recursos de Contenido
son cargados indirectamente mediante la interpretación del
navegador de los elementos HTML; y los Recursos de Descripción,
típicamente, son cargados directamente por JavaScript
usando la interfaz XMLHttpRequest
.
La especificación Cross Origin
Resource Sharing (CORS) implementada por los navegadores
modernos describe las distintas reglas de seguridad
aplicables a las interacciones con esos dos tipos de
recursos.
Dos conceptos adicionales, cookie de acceso y token de acceso, se describen posteriormente.
En este documento, las palabras resaltadas debe(n) y requerido(a)
implican que la definición es un requisito absoluto de la
especificación; la expresión no debe(n)
implica que la definición es una prohibición absoluta de
la especificación; las palabras debiera(n)
y recomendado(a) implican que pueden
existir razones válidas en circunstancias particulares
para ignorar un elemento particular, pero es preciso
comprender y sopesar cuidadosamente todas las
consecuencias antes de seguir un curso diferente; las
expresiones no debiera(n) o no recomendado(a) implican que pueden
existir razones válidas en circunstancias particulares
donde el comportamiento particular es aceptable o útil,
pero es preciso comprender y sopesar cuidadosamente todas
las consecuencias antes de implementarlo; las palabras puede(n) y opcional
implican que el elemento de la especificación es
verdaderamente opcional, si bien la implementación que no
incluya una opción particular debe
estar preparada para interoperar con otra implementación
que sí la incluya (aunque quizás con funcionalidad
reducida) e, inversamente, una implementación que incluya
una opción particular debe estar
preparada para interoperar con otra implementación que no
la incluya (excepto, por supuesto, para la característica
proporcionada por la opción).
1.2. Autenticación para Recursos de Contenido
En general, los Recursos de Contenido (por ejemplo, las
imágenes) son recursos secundarios incrustados en una
página web o aplicación. En las páginas web, las imágenes
se incluyen mediante la etiqueta HTML img
, y se recuperan con
peticiones HTTP
adicionales del navegador. Si un usuario no está
autorizado a cargar una página web, el servidor puede
redirigirlo a otra página y ofrecer la oportunidad de
autenticar. Esa redirección no es posible para Recursos de
Contenido incrustados, y en esos casos se presenta al
usuario un ícono de imagen rota. Si la imagen está sujeta
a control de acceso, el navegador debe evitar imágenes
rotas enviando una cookie que el servidor pueda aceptar
como credencial para conceder acceso a la imagen. Esta
especificación describe como el usuario adquiere esa cookie
de acceso.
1.3. Autenticación para Recursos de Descripción
Los Recursos de Descripción (por ejemplo, un manifest de la API de Presentación, o un documento de información de la API de Imagen (info.json)) proporcionan a la aplicación cliente la información necesaria para que el navegador solicite los Recursos de Contenido. Un Recurso de Descripción debe estar en el mismo dominio que el Recurso de Contenido que describe, pero no es requerido que el código cliente en ejecución esté alojado en ese dominio.
Un navegador que ejecute JavaScript obtenido de un
dominio no puede emplear XMLHttpRequest
para cargar un Recurso de Descripción de otro dominio e
incluir las cookies de ese dominio en la petición, sin
violar el requisito antes mencionado de que el cliente
debe trabajar aunque sea no confiable. En ese
caso, el cliente envía un token de acceso
(técnicamente, un tipo de token al portador) como
sustituto de la cookie de acceso. Esta especificación
describe como, una vez que el navegador consigue la cookie
de acceso para los Recursos de Contenido, el cliente
adquiere el token de acceso para utilizar en las
peticiones directas de Recursos de Descripción.
El servidor en el Dominio de Recurso trata al token de acceso como representación, o sustituto, de la cookie que permite acceder a los Recursos de Contenido. Cuando el cliente solicita Recursos de Descripción y presenta el token de acceso, las respuestas informan al cliente qué sucederá si el navegador solicita los recursos de contenido correspondientes usando la cookie de acceso que el token de acceso representa. Esas respuestas permiten al cliente decidir cuáles interfaz de usuario y/o Recursos de Contenido mostrar al usuario.
1.4. Seguridad
El propósito de esta especificación es soportar el control de acceso para recursos IIIF; por tanto, la seguridad es una tema fundamental. Para impedir su mala utilización, las cookies y los tokens al portador descritos en esta especificación no deben ser revelados en el almacenamiento y el transporte. Las implementaciones debieran usar HTTP sobre TLS, conocido como HTTPS, para todas las comunicaciones. Además, todo cliente IIIF que interactúe con recursos de acceso controlado debiera ser ejecutado desde páginas servidas por medio de HTTPS. Toda referencia a HTTP en esta especificación presupone el uso de HTTPS.
Esta especificación protege los Recursos de Contenido
(por ejemplo, las imágenes) exponiendo el valor del token
de acceso al script de la aplicación cliente, para usar en
la solicitud de Recursos de Descripción. El conocimiento
del token de acceso no es útil para un cliente malicioso,
pues la cookie de acceso (que el cliente no
puede ver) es la única credencial válida para los Recursos
de Contenido, y un Recurso de Descripción carece de valor
por sí solo. No obstante, en versiones futuras, los
patrones de interacción introducidos en esta
especificación se extenderán a operaciones de escritura
sobre recursos IIIF, por ejemplo,
crear anotaciones en un servidor de anotaciones, o
modificar el elemento structures
de un manifest. Para tales operaciones, el token de acceso
es la credencial, y el flujo presentado a
continuación puede requerir uno o más pasos adicionales
para establecer la confianza entre cliente y servidor. Sin
embargo, anticipamos que los cambios serán compatibles
hacia atrás con la versión 1.0.
Más detalles de las consideraciones de seguridad se pueden encontrar en las Notas de Implementación.
2. Servicios de Autenticación
Los servicios de autenticación siguen el patrón descrito
en la nota de IIIF, Enlazando a
Servicios Externos, y son referenciados en uno o más
bloques service
de
las descripciones de los recursos que protegen. Existe un
perfil de servicio primario de inicio de sesión para
autenticar a los usuarios, y en su descripción se anidan
servicios relacionados, que incluyen un servicio
obligatorio de token de acceso, y un servicio opcional de
cierre de sesión.
2.1. Servicio de Cookie de Acceso
El cliente usa este servicio para obtener una cookie que
empleará al interactuar con contenido (por ejemplo,
imágenes), y con el servicio de token de acceso. Existen
varios patrones de interacción en los cuales el cliente
utilizará este servicio, basados en la interfaz de usuario
a renderizar, indicada por una URI de perfil. El cliente
obtiene el enlace hacia el servicio de cookie de acceso de
un bloque service
en una descripción del recurso protegido.
El objetivo del servicio de cookie de acceso es especificar una cookie durante la interacción del usuario con el servidor de contenido, de forma que las peticiones de imagen del cliente al servidor de contenido tengan éxito. El cliente no sabe qué sucede en el servicio de inicio de sesión, y no puede ver las cookies especificadas para el dominio de contenido durante la interacción del usuario con ese servicio. El navegador puede ser redirigido una o más veces, pero eso es invisible para la aplicación cliente. La respuesta final en la pestaña abierta debiera incluir JavaScript que intente cerrar la pestaña, a fin de iniciar el paso siguiente del flujo de trabajo.
2.1.1. Descripción del Servicio
Existen cuatro patrones de interacción mediante los cuales el cliente puede obtener una cookie de acceso, cada uno identificado por una URI de perfil. Esos patrones se describen en las secciones siguientes.
Patrón | URI de Perfil |
Descripción |
---|---|---|
Login | http://iiif.io/api/auth/1/login |
El usuario debe iniciar sesión usando una ventana independiente con una IU provista por un sistema de autenticación externo. |
Clickthrough | http://iiif.io/api/auth/1/clickthrough |
El usuario debe hacer clic en un botón del cliente usando contenido provisto en la descripción del servicio. |
Kiosk | http://iiif.io/api/auth/1/kiosk |
El usuario no necesita interactuar con un sistema de autenticación, se espera que el cliente use el servicio de cookie de acceso automáticamente. |
External | http://iiif.io/api/auth/1/external |
Se espera que el usuario haya adquirido la cookie apropiada; el servicio de cookie de acceso no será usado. |
La descripción del servicio está incluída en el Recurso de Descripción y tiene las siguientes propiedades:
Propiedad | ¿Requerida? | Descripción |
---|---|---|
@context | requerida | Documento de contexto que describe la API
de Autenticación de IIIF.
El valor debe ser http://iiif.io/api/auth/1/context.json . |
@id | ver descripción | requerida para los patrones Login, Clickthrough, y Kiosk, donde el cliente abre la URI para obtener una cookie de acceso. opcional para el patrón External, pues se asume que el usuario consiguió la cookie por otros medios, y se ignora cualquier valor provisto. |
profile | requerida | El perfil del servicio debe ser una de las URIs de perfil de la tabla anterior. |
label | requerida | Texto a mostrar al usuario para dar inicio a la carga del servicio de autenticación cuando se requieren múltiples servicios. El valor debe incluir el dominio o institución donde el usuario se está autenticando. |
confirmLabel | recomendada | Texto a mostrar al usuario en el botón o elemento que abre el servicio de cookie de acceso. Si no está presente, el cliente proporciona texto adecuado para el patrón de interacción, de ser necesario. |
header | recomendada | Texto breve que, de estar presente, debe ser mostrado al usuario como un header de la descripción, o en solitario, si no hay descripción. |
description | recomendada | Texto que, de estar presente, debe ser mostrado al usuario antes de abrir el servicio de cookie de acceso. |
failureHeader | opcional | Texto breve que, de estar presente, puede ser mostrado al usuario como un header si no se recibe un token, o si usar el token genera un error. |
failureDescription | opcional | Texto que, de estar presente, puede ser mostrado al usuario si no se recibe un token, o si usar el token genera un error. |
service | requerida | Referencias a token de acceso y otros servicios relacionados, descritos más adelante. |
2.1.2. Interacción con el Servicio de Cookie de Acceso
El cliente debe adjuntar el siguiente parámetro de consulta a toda petición de una URI de servicio de cookie de acceso, con independencia del patrón de interacción, y abrir esa URI en una nueva ventana o pestaña.
Parámetro | Descripción |
---|---|
origin | String que contiene el origen de la página en la ventana, consistente de protocolo, nombre de host, y número de puerto opcional, como describe la especificación de la API postMessage. |
Por ejemplo, dada la URI de servicio de cookie de acceso
https://authentication.example.org/login
,
un cliente instanciado por la página https://client.example.com/viewer/index.html
haría su petición a:
https://authentication.example.org/login?origin=https://client.example.com/
El servidor puede usar esa información para validar el origen provisto en peticiones subsiguientes al servicio de token de acceso, por ejemplo, codificándolo en la cookie retornada.
2.1.3. Patrón de Interacción Login
Para inducir al usuario a iniciar sesión, el cliente debe
mostrar parte de la interfaz de usuario del proveedor de
contenido. Para el patrón de interacción Login, el valor
de la propiedad @id
es la URI de esa interfaz de usuario.
La interacción tiene los pasos siguientes:
- Si las propiedades
header
y/odescription
están presentes, antes de abrir la interfaz de autenticación del proveedor, el cliente debiera mostrar los valores de las propiedades al usuario. Las propiedades describen qué sucederá al hacer clic en el elemento deconfirmLabel
. - Tras la activación del elemento de
confirmLabel
, el cliente debe abrir la URI de@id
con el parámetro de consultaorigin
añadido. Eso se debe realizar en una nueva ventana o pestaña para contribuir a la prevención de ataques de spoofing. Las reglas de seguridad del navegador impiden que el cliente sepa lo que sucede en la nueva pestaña; por tanto, el cliente sólo puede esperar por, y detectar, el cierre de la pestaña abierta. - Tras el cierre de la pestaña abierta, el cliente debe usar el servicio de token de acceso relacionado, como describimos más adelante.
Con conocimiento externo, los clientes autorizados no
dirigidos por el usuario pueden usar
POST para enviar la información del usuario
pre-autenticado al servicio. Como la información requerida
depende de la lógica de autorización, esta API no
especifica los detalles.
Un ejemplo de descripción de servicio para el patrón de interacción Login:
{
// ...
"service" : {
"@context": "http://iiif.io/api/auth/1/context.json",
"@id": "https://authentication.example.org/login",
"profile": "http://iiif.io/api/auth/1/login",
"label": "Inicio de Sesión en la Institución Ejemplo",
"header": "Por Favor, Inicie Sesión",
"description": "La Institución Ejemplo requiere que inicie sesión en su cuenta de Ejemplo para ver este contenido.",
"confirmLabel": "Inicio de Sesión",
"failureHeader": "Autenticación Fallida",
"failureDescription": "<a href=\"http://example.org/policy\">Política de Acceso</a>",
"service": [
// Servicios de token de acceso y cierre de sesión ...
]
}
}
2.1.4. Patrón de Interacción Clickthrough
En el patrón de interacción Clickthrough, el valor de la
propiedad @id
es
la URI de un servicio que debe
especificar una cookie de acceso y cerrar inmediatamente
su ventana o pestaña sin intervención del usuario. La
interacción tiene los pasos siguientes:
- Si las propiedades
header
y/odescription
están presentes, antes de abrir el servicio, el cliente debe mostrar los valores de las propiedades al usuario. Las propiedades describen el acuerdo implícito al hacer clic en el elemento deconfirmLabel
. - Tras la activación del elemento de
confirmLabel
, el cliente debe abrir la URI de@id
con el parámetro de consultaorigin
añadido. Esto se debiera realizar en una nueva ventana o pestaña. Las reglas de seguridad del navegador impiden que el cliente sepa lo que sucede en la nueva pestaña; por tanto, el cliente sólo puede esperar por, y detectar, el cierre de la ventana, pestaña, o iframe abierta. - Tras el cierre de la pestaña abierta, el cliente debe usar el servicio de token de acceso relacionado, como describimos más adelante.
Los clientes no dirigidos por el usuario no deben usar servicios de cookie de acceso con el patrón de interacción Clickthrough; en lugar de eso, se deben detener.
Un ejemplo de descripción de servicio para el patrón de interacción Clickthrough:
{
// ...
"service" : {
"@context": "http://iiif.io/api/auth/1/context.json",
"@id": "https://authentication.example.org/clickthrough",
"profile": "http://iiif.io/api/auth/1/clickthrough",
"label": "Condiciones de Uso para la Institución Ejemplo",
"header": "Material Restringido por Condiciones de Uso",
"description": "<span>... condiciones de uso ... </span>",
"confirmLabel": "Acepto",
"failureHeader": "Condiciones de Uso No Aceptadas",
"failureDescription": "Debe aceptar las condiciones de uso para ver el contenido.",
"service": {
// Servicio de token de acceso ...
}
}
}
2.1.5. Patrón de Interacción Kiosk
En el patrón de interacción Kiosk, el valor de la
propiedad @id
es
la URI de un servicio que debe
especificar una cookie de acceso y cerrar inmediatamente
su ventana o pestaña sin intervención del usuario. La
interacción tiene los pasos siguientes:
- No hay intervención del usuario antes de abrir la URI
del servicio de cookie de acceso; por tanto, las
propiedades
label
,header
,description
yconfirmLabel
se ignoran, de estar presentes. - El cliente debe abrir
inmediatamente la URI de
@id
con el parámetro de consultaorigin
añadido. Eso se debiera realizar en una nueva ventana o pestaña. Las reglas de seguridad del navegador impiden que el cliente sepa lo que sucede en la nueva pestaña; por tanto, el cliente sólo puede esperar por, y detectar, el cierre de la ventana, pestaña, o iframe abierta. - Tras el cierre de la pestaña abierta, el cliente debe usar el servicio de token de acceso relacionado, como describimos más adelante.
Los clientes no dirigidos por el usuario acceden a la URI
de @id
para
obtener la cookie de acceso, y usan el servicio de token
de acceso relacionado, como describimos más adelante.
Un ejemplo de descripción de servicio para el patrón de interacción Kiosk:
{
// ...
"service" : {
"@context": "http://iiif.io/api/auth/1/context.json",
"@id": "https://authentication.example.org/cookiebaker",
"profile": "http://iiif.io/api/auth/1/kiosk",
"label": "Servicio interno de concesión de cookies",
"failureHeader": "Ups!",
"failureDescription": "Llamar a Bob a la ext. 1234 para reiniciar el servidor de cookies",
"service": {
// Servicio de token de acceso ...
}
}
}
2.1.6. Patrón de Interacción External
En el patrón de interacción External, el usuario debe haber adquirido la cookie de acceso por medios externos. Si la cookie de acceso no está presente, el usuario recibe mensajes de fallo. La interacción tiene los pasos siguientes:
- No hay intervención del usuario antes de abrir la URI
del servicio de token de acceso; por
tanto, las propiedades
label
,header
,description
yconfirmLabel
se ignoran, de estar presentes. - No hay servicio de cookie de acceso. Cualquier URI
especificada en la propiedad
@id
se debe ignorar. - El cliente debe usar inmediatamente el servicio de token de acceso relacionado, como describimos más adelante.
Los clientes no dirigidos por el usuario usan el servicio de token de acceso relacionado, como describimos más adelante, con una cookie de acceso adquirida previamente.
Un ejemplo de descripción de servicio para el patrón de interacción External:
{
// ...
"service" : {
"@context": "http://iiif.io/api/auth/1/context.json",
"profile": "http://iiif.io/api/auth/1/external",
"label": "Autenticación Externa Requerida",
"failureHeader": "Material Restringido",
"failureDescription": "Este material no es visible sin acuerdo previo",
"service": {
// Servicio de token de acceso ...
}
}
}
2.2. Servicio de Token de Acceso
El cliente usa este servicio para obtener un token de acceso que empleará al solicitar Recursos de Descripción. Una petición al servicio de token de acceso debe incluir cualquier cookie para el dominio de contenido adquirida en la interacción del usuario con el servicio de cookie de acceso correspondiente, para que el servidor pueda generar el token de acceso.
2.2.1. Descripción del Servicio
La descripción del servicio de cookie de acceso debe incluir una descripción de servicio de token de acceso obedeciendo el patrón siguiente:
{
// Servicio de Cookie de Acceso
"service" : {
"@context": "http://iiif.io/api/auth/1/context.json",
"@id": "https://authentication.example.org/login",
"profile": "http://iiif.io/api/auth/1/login",
"label": "Iniciar Sesión en la Institución Ejemplo",
// Servicio de Token de Acceso
"service": [
{
"@id": "https://authentication.example.org/token",
"profile": "http://iiif.io/api/auth/1/token"
}
]
}
}
La propiedad @id
del servicio de token de acceso debe
estar presente, y su valor debe ser
la URI donde el cliente puede obtener el token de acceso.
La propiedad profile
debe estar presente, y su valor debe ser http://iiif.io/api/auth/1/token
para distinguirla de otros servicios. No es necesario
repetir la propiedad @context
incluída en la descripción de servicio de cookie de acceso
correspondiente, y no hay otras propiedades para este
servicio.
2.2.2. Respuesta JSON de Token de Acceso
Si la petición incluye una cookie válida que el servidor reconoce como generada por el servicio de cookie de acceso, la respuesta del servicio de token de acceso debe incluir un objeto JSON (no JSON-LD) con la siguiente estructura:
{
"accessToken": "TOKEN_AQUÍ",
"expiresIn": 3600
}
La propiedad accessToken
es requerida, y su valor es el token
de acceso para pasar en peticiones futuras. La propiedad expiresIn
es opcional y, de estar presente, su valor
es la cantidad de segundos hasta que el token de acceso
deje de ser válido.
Una vez obtenido, el token de acceso debe
ser pasado al servidor en toda petición futura de Recurso
de Descripción añadiendo un header de petición Authorization
, con el
valor Bearer
seguido de un espacio y el token de acceso, de esta
manera:
Authorization: Bearer TOKEN_AQUÍ
Ese header de autorización se debiera añadir a toda petición de recursos del mismo dominio y subdominios, que referencien al servicio, con independencia de la API con la cual se interactúe. no debe ser enviado a otros dominios.
2.2.3. Interacción para Aplicaciones Cliente No Basadas en Navegador
La petición de token de acceso más simple procede de un cliente no basado en navegador que pueda enviar cookies entre dominios, donde las restricciones CORS no son aplicables. Esta URL:
https://authentication.example.org/token
Produciría la Petición HTTP:
GET /token HTTP/1.1
Cookie: <cookie-adquirida-durante-login>
La respuesta es el objeto JSON de token de acceso con tipo de
medio application/json
:
{
"accessToken": "TOKEN_AQUÍ",
"expiresIn": 3600
}
2.2.4. Interacción para Aplicaciones Cliente Basadas en Navegador
Si el cliente es una aplicación de JavaScript que se
ejecuta en un navegador web, debe solicitar directamente
el token de acceso y almacenar el resultado. El cliente no
puede utilizar XMLHttpRequest
porque no puede incluir la cookie de acceso en una
petición entre dominios. En su lugar, el cliente debe abrir el servicio de token de
acceso en un frame, usando un elemento iframe
, y estar listo
para recibir un mensaje publicado por script en ese frame
mediante la API postMessage.
Para provocar ese comportamiento, el cliente debe adjuntar los siguientes parámetros
de consulta a la URI del servicio de token de acceso, y
abrir la nueva URI en el frame.
Parámetro | Descripción |
---|---|
messageId | String que ordena al servidor responder con una
página web en lugar de JSON, y permite al cliente
hacer corresponder peticiones de servicio de token
de acceso y mensajes recibidos. Si el cliente no
necesita interactuar con múltiples servicios de
token, puede asignar un valor ficticio al parámetro,
por ejemplo, messageId=1 . |
origin | String que contiene el origen de la página en la ventana, consistente de protocolo, nombre de host, y número de puerto opcional, como describe la especificación de la API postMessage. |
Por ejemplo, un cliente instanciado por la página https://client.example.com/viewer/index.html
solicitaría:
https://authentication.example.org/token?messageId=1&origin=https://client.example.com/
Ante una petición de servicio de token de acceso con el
parámetro messageId
,
el servidor debe responder con una
página web HTML,
no JSON
puro. La página web debe contener un
script que envíe un mensaje a la página que se abre,
mediante la API postMessage. El
cuerpo del mensaje es el objeto JSON de token de acceso, con el
valor suministrado de messageId
como propiedad adicional (vea los ejemplos de la sección
siguiente).
El servidor puede usar la
información de origen para lógica de autorización
adicional, aunque el usuario ya esté autenticado. Por
ejemplo, el servidor puede confiar sólo en dominios
específicos para acciones distintas de la simple lectura,
como crear o eliminar recursos. Si el cliente envía un
valor incorrecto, no recibe respuesta, pues la API
postMessage no despacha el evento. El parámetro targetOrigin
de la
función postMessage()
debe ser el origen provisto en la
petición.
El frame no debiera ser mostrado al usuario. Es un mecanismo de mensajería entre dominios. El cliente debe registrar un listener de eventos para recibir el mensaje que la página del servicio de token en el frame enviará. El cliente puede reutilizar los mismos listener y frame para múltiples llamadas al servicio de token de acceso, o puede crear nuevos en cada invocación.
La implementación precisa varía pero debe incluir características equivalentes a los pasos siguientes.
Primero, el cliente debe registrar un listener de eventos para recibir mensajes de otros dominios:
window.addEventListener("message", receive_message);
function receive_message(event) {
data = event.data;
var token, error;
if (data.hasOwnProperty('accessToken')) {
token = data.accessToken;
} else {
// manejar las condiciones de error
}
// ...
}
Después, puede abrir el servicio de token de acceso en un frame:
document.getElementById('messageFrame').src =
'https://authentication.example.org/token?messageId=1234&origin=https://client.example.com/';
La respuesta del servidor será una página web con tipo de
medio text/html
que puede enviar un mensaje al listener registrado:
<html>
<body>
<script>
window.parent.postMessage(
{
"messageId": "1234",
"accessToken": "TOKEN_AQUÍ",
"expiresIn": 3600
},
'https://client.example.com/'
);
</script>
</body>
</html>
2.2.5. Usando el Token de Acceso
El token de acceso es enviado en toda petición subsiguiente de Recurso de Descripción. Por ejemplo, una petición de información de imagen en la API de Imagen sería:
GET /iiif/identifier/info.json HTTP/1.1
Authorization: Bearer TOKEN_AQUÍ
2.2.6. Condiciones de Error del Token de Acceso
La respuesta del servicio de token de acceso puede ser un
error. El error se debe suministrar
como JSON
cumpliendo el patrón siguiente. Para clientes basados en
navegador que utilizan la API postMessage, el objeto
de error se debe enviar al cliente mediante JavaScript,
del mismo modo que se envía el token de acceso. Para
peticiones directas, el cuerpo de la respuesta es JSON puro.
{
"error": "TIPO_DE_ERROR_AQUÍ",
"description": "..."
}
El valor de la propiedad error
debe ser uno de los tipos de la tabla
siguiente:
Tipo | Descripción |
---|---|
invalidRequest |
El servicio no puede procesar la información enviada en el cuerpo de la petición. |
missingCredentials |
La petición no tiene las credenciales requeridas. |
invalidCredentials |
Las credenciales de la petición no son válidas para el servicio. |
invalidOrigin |
La petición tiene un origen distinto al especificado en la petición del servicio de cookie de acceso, o un origen que el servidor rechaza por otros motivos. |
unavailable |
La petición no se puede satisfacer por razones distintas a las anteriores, por ejemplo, mantenimiento programado. |
La propiedad description
es opcional, y puede contener
información adicional legible por humanos sobre el error.
Al retornar JSON directamente, el servicio debe usar el código de estado HTTP apropiado en la respuesta para describir el error (por ejemplo, 400, 401, o 503). La respuesta de página web postMessage debe usar el código de estado HTTP 200 para asegurar que el cuerpo sea recibido correctamente por el cliente.
2.3. Servicio de Cierre de Sesión
En el caso del patrón de interacción Login, el cliente debe saber si el usuario puede cerrar sesión, y donde puede hacerlo. Por ejemplo, el usuario podría querer cerrar su sesión en un terminal público, o iniciar sesión nuevamente en una cuenta distinta.
2.3.1. Descripción del Servicio
Si el sistema de autenticación soporta el cierre de sesión intencional de los usuarios, debiera existir un servicio de cierre de sesión asociado al servicio de cookie de acceso, que cumpla el patrón siguiente:
{
// ...
"service" : {
"@context": "http://iiif.io/api/auth/1/context.json",
"@id": "https://authentication.example.org/login",
"profile": "http://iiif.io/api/auth/1/login",
"label": "Iniciar Sesión en la Institución Ejemplo",
"service" : [
{
"@id": "https://authentication.example.org/token",
"profile": "http://iiif.io/api/auth/1/token"
},
{
"@id": "https://authentication.example.org/logout",
"profile": "http://iiif.io/api/auth/1/logout",
"label": "Cerrar Sesión en la Institución Ejemplo"
}
]
}
}
El valor de la propiedad profile
debe ser http://iiif.io/api/auth/1/logout
.
2.3.2. Interacción
El cliente debiera mostrar el
resultado de una petición HTTP GET
de la URI del servicio en una pestaña o ventana aparte,
que incluya barra de URL. Al mismo tiempo, el cliente debiera descartar cualquier token de
acceso que haya recibido del servicio correspondiente. El
servidor debiera anular el estado de
sesión iniciada del usuario cuando reciba esta petición, y
eliminar la cookie de acceso.
2.4. Ejemplo de Recurso de Descripción con Servicios de Autenticación
El ejemplo siguiente es una respuesta completa de información de imagen, para una imagen de ejemplo, con todos los servicios de autenticación.
{
"@context" : "http://iiif.io/api/image/2/context.json",
"@id" : "https://www.example.org/images/image1",
"protocol" : "http://iiif.io/api/image",
"width" : 600,
"height" : 400,
"sizes" : [
{"width" : 150, "height" : 100},
{"width" : 600, "height" : 400}
],
"profile" : [
"http://iiif.io/api/image/2/level2.json",
{
"formats" : [ "gif", "pdf" ],
"qualities" : [ "color", "gray" ],
"supports" : [
"canonicalLinkHeader", "rotationArbitrary"
]
}
],
"service" : {
"@context": "http://iiif.io/api/auth/1/context.json",
"@id": "https://authentication.example.org/login",
"profile": "http://iiif.io/api/auth/1/login",
"label": "Iniciar Sesión en la Institución Ejemplo",
"service" : [
{
"@id": "https://authentication.example.org/token",
"profile": "http://iiif.io/api/auth/1/token"
},
{
"@id": "https://authentication.example.org/logout",
"profile": "http://iiif.io/api/auth/1/logout",
"label": "Cerrar Sesión en la Institución Ejemplo"
}
]
}
}
3.
Interacción con Recursos de Acceso Controlado
Esta sección describe como los clientes usan los servicios anteriores al interactuar con Recursos de Contenido y Recursos de Descripción.
Esas interacciones dependen de que las peticiones de
Recursos de Descripción retornen códigos de estado HTTP 200
, 302
, y 401
en diferentes
circunstancias. Para códigos distintos de 302
, el cuerpo de la
respuesta debe ser un Recurso de
Descripción válido porque el cliente necesita ver las
descripciones del servicio de autenticación para seguir el
flujo de trabajo apropiado. Una respuesta con código de
estado 302
no será
visible para scripts de clientes basados en navegador que
interactúen mediante la API XMLHttpRequest
. La
respuesta reportada será la última en la cadena y, por
tanto, no es necesario que el cuerpo de las respuestas de
redirección sea la representación del Recurso de
Descripción.
3.1. Acceso Todo o Nada
Si el servidor no soporta múltiples niveles de acceso a
un Recurso de Contenido, y el usuario no está autorizado a
acceder, el servidor debe retornar
una respuesta con código de estado HTTP 401
(Unauthorized) para
el Recurso de Descripción correspondiente.
Si el usuario está autorizado a acceder a un Recurso de Descripción, el cliente puede asumir que acceder a los Recursos de Contenido descritos está permitido también. Las peticiones de Recursos de Contenido dependen de la cookie de acceso para transmitir el estado de autorización.
3.2. Acceso por Niveles
Un servidor que soporte varios niveles de acceso, debe usar un identificador distinto
para cada Recurso de Descripción y sus Recursos de
Contenido correspondientes. Por ejemplo, para cada nivel debe haber documentos de información de
imagen (/info.json
)
diferentes en URIs distintas. Al referenciar Recursos de
Descripción con varios niveles de acceso, los sistemas debieran usar el identificador de la
versión que el usuario autorizado identificado debiera
ver. Por ejemplo, al referenciar un servicio de imagen
desde un manifest, la referencia normal sería a la versión
en calidad máxima de la imagen, no a una versión
degradada.
Si un usuario solicita un Recurso de Descripción que no
está autorizado a acceder, y existen niveles inferiores
disponibles, el servidor debe enviar
una respuesta de estado HTTP 302
(Found) para
redirigir hacia el Recurso de Descripción de un nivel
menor.
Si el usuario no está autorizado a acceder a un Recurso
de Descripción, y no existen niveles menores, el servidor
debe enviar una respuesta 401
(Unauthorized). El
cliente debiera presentar
información sobre los servicios Login y/o Clickthrough
incluídos en el Recurso de Descripción para permitir la
autenticación del usuario.
4. Flujo de Trabajo desde la Perspectiva del Cliente Navegador
1 Flujo de Trabajo de la Autenticación del Cliente |
Los clientes basados en navegador seguirán este flujo de trabajo para acceder a recursos de acceso controlado:
- El cliente solicita el Recurso de Descripción y verifica el código de estado de la respuesta.
- Si la respuesta es 200,
- El cliente comprueba que la propiedad
@id
de la respuesta es la URI solicitada. - De ser así, el cliente puede solicitar el Recurso de Contenido.
- Si las URIs son distintas, el recurso es de un nivel diferente al solicitado. El estado 200 implica que el recurso puede ser usado y, por tanto, el cliente puede renderizar el recurso. Al mismo tiempo, el cliente busca servicios de autenticación en el JSON recibido.
- El cliente comprueba que la propiedad
- Si la respuesta es 401,
- El cliente no tiene acceso al Recurso de Contenido, y debe buscar servicios de autenticación en el JSON recibido.
-
Si la respuesta no es 200 ni 401, el cliente debe manejar otros códigos de estado HTTP
- Al buscar servicios de autenticación, el cliente:
- Busca un patrón de interacción External que no requiere interacción de usuario. Si está presente, abre el servicio de token de acceso para determinar si ya se obtuvo la cookie.
- En otro caso, busca un patrón de interacción Kiosk que no requiere interacción de usuario. Si está presente, abre el servicio de cookie de acceso en una ventana aparte.
- En otro caso, busca un patrón de interacción Clickthrough. Si está presente, renderiza la descripción del servicio y un botón de confirmación para obtener el click-through del usuario. Tras el clic del usuario en la confirmación, abre el servicio de cookie de acceso en una ventana aparte.
- En otro caso, presenta los patrones de interacción Login disponibles, y pide al usuario iniciar sesión con uno de ellos. Tras la selección, por parte del usuario, del ámbito para iniciar sesión, que asume el papel del servicio de cookie de acceso, abre la interfaz de usuario del ámbito en una ventana aparte.
- Tras el cierre, automático o manual, de la ventana del servicio de cookie de acceso, el cliente abre el servicio de token de acceso.
- Tras pedir el servicio de token de acceso, si el
cliente recibe un token, intenta leer el Recurso de
Descripción con las nuevas credenciales adquiridas.
- En cambio, si recibe un error, el cliente busca otros servicios de autenticación para interactuar.
- Si no quedan servicios de autenticación, las credenciales del usuario no permiten interactuar con ninguna versión del Recurso de Contenido, y el cliente no puede mostrar nada.
Por favor, note que la implementación de servidor incluye
proporcionar respuestas de estado 302
para redirigir al
cliente desde el nivel solicitado a otro nivel mientras el
usuario no esté autorizado a ver el recurso. El cliente
basado en navegador no ve esas respuestas y, por tanto, no
revisa el código de estado HTTP retornado; sólo verifica
si el identificador del recurso es igual al solicitado.
Apéndices
A. Notas de Implementación
Se ofrecen orientaciones para los implementadores en el documento Notas de Implementación. Esas notas contienen muchos detalles relacionados a la implementación de esta especificación en aplicaciones de JavaScript basadas en navegadores, y consideraciones adicionales sobre seguridad.
B. Control de Versiones
A partir de la versión 0.9.0, esta especificación sigue Semantic Versioning. Vea la nota Control de Versiones de APIs para los detalles de implementación.
C. Agradecimientos
La producción de este documento fue apoyada generosamente por una donación de la Fundación Andrew W. Mellon.
Muchas gracias a los miembros de la Comunidad IIIF por su continuo compromiso, ideas innovadoras, y retroalimentación.
D. Registro de Cambios
Fecha |
Descripción |
---|---|
2017-01-19 | Versión 1.0 (Alchemical Key) |
2016-10-05 | Versión 0.9.4 (Incrementing Integer) ampliar notas de seguridad |
2016-08-22 | Versión 0.9.3 (Wasabi KitKat) separar perfiles, eliminar servicio de identidad de cliente, añadir parámetros de consulta |
(no publicada) | Versión 0.9.2 (sin nombre) postMessage en lugar de JSONP |
2015-10-30 | Versión 0.9.1 (Table Flip) añadir @context faltante, aclaraciones |
2015-07-28 | Versión 0.9.0 (sin nombre) borrador |