Cuando Looker se inserta en un iframe mediante la inserción firmada, algunos navegadores usan de forma predeterminada una política de cookies que bloquea las cookies de terceros. Las cookies de terceros se rechazan cuando el iframe insertado se carga desde un dominio distinto al que carga la aplicación de inserción. Por lo general, puede solucionar esta limitación solicitando y usando un dominio personalizado. Sin embargo, los dominios personalizados no se pueden usar en algunos casos. En estos casos, se puede usar la inserción sin cookies de Looker.
¿Cómo funciona la inserción sin cookies?
Cuando las cookies de terceros no están bloqueadas, se crea una cookie de sesión cuando un usuario inicia sesión en Looker por primera vez. Esta cookie se envía con cada solicitud de usuario y el servidor de Looker la usa para establecer la identidad del usuario que ha iniciado la solicitud. Cuando se bloquean las cookies, no se envían con una solicitud, por lo que el servidor de Looker no puede identificar al usuario asociado a la solicitud.
Para solucionar este problema, la inserción sin cookies de Looker asocia tokens a cada solicitud que se puede usar para recrear la sesión de usuario en el servidor de Looker. La aplicación de inserción es la responsable de obtener estos tokens y ponerlos a disposición de la instancia de Looker que se ejecuta en el iframe insertado. El proceso para obtener y proporcionar estos tokens se describe en el resto de este documento.
Para usar cualquiera de las APIs, la aplicación de inserción debe poder autenticarse en la API de Looker con privilegios de administrador. El dominio de inserción también debe figurar en la lista de permitidos de dominios de inserción o, si se usa Looker 23.8 o una versión posterior, se puede incluir cuando se adquiera la sesión sin cookies.
Crear un iframe de inserción de Looker
En el siguiente diagrama de secuencia se ilustra la creación de un iframe insertado. Se pueden generar varios iframes simultáneamente o en algún momento en el futuro. Si se implementa correctamente, el iframe se unirá automáticamente a la sesión creada por el primer iframe. El SDK de Looker Embed simplifica este proceso uniendo automáticamente la sesión actual.
- El usuario realiza una acción en la aplicación insertada que da como resultado la creación de un iframe de Looker.
- El cliente de la aplicación insertada adquiere una sesión de Looker. El SDK de Looker Embed se puede usar para iniciar esta sesión, pero se debe proporcionar una URL de endpoint o una función de retrollamada. Si se usa una función de retrollamada, llamará al servidor de la aplicación insertada para obtener la sesión de inserción de Looker. De lo contrario, el SDK de inserción llamará a la URL del endpoint proporcionada.
- El servidor de la aplicación de inserción usa la API de Looker para obtener una sesión de inserción. Esta llamada a la API es similar al proceso de firma de inserciones firmadas de Looker, ya que acepta la definición de usuario insertado como entrada. Si ya existe una sesión de Looker insertada para el usuario que llama, el token de referencia de sesión asociado debe incluirse en la llamada. Esto se explicará con más detalle en la sección Adquirir sesión de este documento.
- El procesamiento del endpoint de la sesión de inserción de adquisición es similar al del endpoint firmado
/login/embed/(signed url)
, ya que espera la definición del usuario de inserción de Looker como cuerpo de la solicitud, en lugar de en la URL. El proceso del endpoint de sesión de adquisición de inserción valida y, a continuación, crea o actualiza el usuario de inserción. También puede aceptar un token de referencia de sesión. Esto es importante porque permite que varios iframes insertados de Looker compartan la misma sesión. El usuario insertado no se actualizará si se proporciona un token de referencia de sesión y la sesión no ha caducado. De esta forma, se admite el caso práctico en el que se crea un iframe con una URL de inserción firmada y otros iframes sin una URL de inserción firmada. En este caso, los iframes sin URLs de inserción firmadas heredarán la cookie de la primera sesión. - La llamada a la API de Looker devuelve cuatro tokens, cada uno con un tiempo de vida (TTL):
- Token de autorización (TTL = 30 segundos)
- Token de navegación (TTL = 10 minutos)
- Token de API (TTL = 10 minutos)
- Token de referencia de sesión (TTL = tiempo de vida restante de la sesión)
- El servidor de aplicaciones de inserción debe registrar los datos que devuelve Looker y asociarlos tanto al usuario que llama como al agente de usuario del navegador del usuario que llama. En la sección Generar tokens de este documento se ofrecen sugerencias sobre cómo hacerlo. Esta llamada devolverá el token de autorización, un token de navegación y un token de API, junto con todos los TTLs asociados. El token de referencia de sesión debe protegerse y no exponerse en el navegador que realiza la llamada.
Una vez que los tokens se hayan devuelto al navegador, se debe crear una URL de inicio de sesión de Looker insertado. El SDK de Looker para insertar contenido creará automáticamente la URL de inicio de sesión insertada. Para usar la API
windows.postMessage
para crear la URL de inicio de sesión insertado, consulta la sección Usar la APIwindows.postMessage
de Looker de este documento para ver ejemplos.La URL de inicio de sesión no contiene los detalles del usuario insertado con firma. Contiene el URI de destino, incluido el token de navegación, y el token de autorización como parámetro de consulta. El token de autorización debe usarse en un plazo de 30 segundos y solo se puede usar una vez. Si se necesitan más iframes, se debe volver a obtener una sesión de inserción. Sin embargo, si se proporciona el token de referencia de sesión, el token de autorización se asociará a la misma sesión.
El endpoint de inicio de sesión de Looker determina si el inicio de sesión es para una inserción sin cookies, que se indica mediante la presencia del token de autorización. Si el token de autorización es válido, comprueba lo siguiente:
- La sesión asociada sigue siendo válida.
- El usuario insertado asociado sigue siendo válido.
- El user-agent del navegador asociado a la solicitud coincide con el agente del navegador asociado a la sesión.
Si las comprobaciones del paso anterior se superan, la solicitud se redirige mediante el URI de destino que se incluye en la URL. Este proceso es el mismo que el del inicio de sesión de la inserción firmada de Looker.
Esta solicitud es la redirección para iniciar el panel de control de Looker. Esta solicitud tendrá el token de navegación como parámetro.
Antes de que se ejecute el endpoint, el servidor de Looker busca el token de navegación en la solicitud. Si el servidor encuentra el token, comprueba lo siguiente:
- La sesión asociada sigue siendo válida.
- El user-agent del navegador asociado a la solicitud coincide con el agente del navegador asociado a la sesión.
Si es válida, la sesión se restaura para la solicitud y se ejecuta la solicitud del panel de control.
El HTML para cargar el panel de control se devuelve al iframe.
La interfaz de usuario de Looker que se ejecuta en el iframe determina que el HTML del panel de control es una respuesta de inserción sin cookies. En ese momento, la interfaz de Looker envía un mensaje a la aplicación insertada para solicitar los tokens que se obtuvieron en el paso 6. A continuación, la interfaz de usuario espera hasta recibir los tokens. Si no llegan los tokens, se muestra un mensaje.
La aplicación de inserción envía los tokens al iframe de Looker insertado.
Cuando se reciben los tokens, la interfaz de usuario de Looker que se ejecuta en el iframe inicia el proceso para renderizar el objeto de solicitud. Durante este proceso, la interfaz de usuario hará llamadas a la API al servidor de Looker. El token de API que se ha recibido en el paso 15 se inserta automáticamente como encabezado en todas las solicitudes de API.
Antes de ejecutar cualquier endpoint, el servidor de Looker busca el token de API en la solicitud. Si el servidor encuentra el token, comprueba lo siguiente:
- La sesión asociada sigue siendo válida.
- El user-agent del navegador asociado a la solicitud coincide con el agente del navegador asociado a la sesión.
Si la sesión es válida, se restaura para la solicitud y se ejecuta la solicitud a la API.
Se devuelven los datos del panel de control.
Se renderiza el panel de control.
El usuario tiene control sobre el panel de control.
Generando tokens nuevos
En el siguiente diagrama de secuencia se muestra la generación de nuevos tokens.
- La interfaz de Looker que se ejecuta en el iframe insertado monitoriza el TTL de los tokens de inserción.
- Cuando los tokens se acercan a la fecha de vencimiento, la interfaz de usuario de Looker envía un mensaje de token de actualización al cliente de la aplicación insertada.
- A continuación, el cliente de la aplicación insertada solicita nuevos tokens a un endpoint que se implementa en el servidor de la aplicación insertada. El SDK de Looker para insertar solicitará nuevos tokens automáticamente, pero se debe proporcionar la URL del endpoint o una función de retrollamada. Si se usa la función de retrollamada, llamará al servidor de la aplicación insertada para generar nuevos tokens. De lo contrario, el SDK de inserción llamará a la URL del endpoint proporcionada.
- La aplicación de inserción busca el
session_reference_token
asociado a la sesión de inserción. En el ejemplo que se proporciona en el repositorio Git del SDK de Looker para insertar se usan cookies de sesión, pero también se puede usar una caché distribuida del lado del servidor, como Redis. - El servidor de la aplicación insertada llama al servidor de Looker con una solicitud para generar tokens. Esta solicitud también requiere tokens de API y de navegación recientes, además del user-agent del navegador que ha iniciado la solicitud.
- El servidor de Looker valida el user-agent, el token de referencia de sesión, el token de navegación y el token de API. Si la solicitud es válida, se generan tokens nuevos.
- Los tokens se devuelven al servidor de la aplicación de inserción que ha llamado.
- El servidor de la aplicación insertada elimina el token de referencia de sesión de la respuesta y devuelve el resto de la respuesta al cliente de la aplicación insertada.
- El cliente de la aplicación insertada envía los tokens recién generados a la interfaz de Looker. El SDK de Looker para inserciones lo hará automáticamente. Los clientes de aplicaciones insertadas que usen la API
windows.postMessage
serán responsables de enviar los tokens. Una vez que la interfaz de usuario de Looker recibe los tokens, se utilizan en las llamadas a la API y en las navegaciones de páginas posteriores.
Implementar la inserción sin cookies de Looker
La inserción sin cookies de Looker se puede implementar mediante el SDK de inserción de Looker o la API windows.postMessage
. Puedes usar el método SDK de inserción de Looker, pero también hay disponible un ejemplo que muestra cómo usar la API windows.postMessage
. Puede consultar explicaciones detalladas de ambas implementaciones en el archivo README del SDK de inserción de Looker. El repositorio de Git del SDK de inserción también contiene implementaciones funcionales.
Configurar la instancia de Looker
La inserción sin cookies tiene elementos en común con la inserción firmada de Looker. Para usar la inserción sin cookies, un administrador debe habilitar Insertar autenticación SSO. Sin embargo, a diferencia de la inserción firmada de Looker, la inserción sin cookies no usa el ajuste Secreto de inserción. La inserción sin cookies usa un JSON Web Token (JWT) en forma de ajuste Insertar secreto de JWT, que se puede definir o restablecer en la página Insertar de la sección Plataforma del menú Administrar.
No es necesario definir el secreto de JWT, ya que se creará en el primer intento de crear una sesión insertada sin cookies. No restablezcas este token, ya que, si lo haces, se invalidarán todas las sesiones de inserción activas sin cookies.
A diferencia del secreto de inserción, el secreto de JWT de inserción nunca se expone, ya que solo se usa internamente en el servidor de Looker.
Implementación de clientes de aplicaciones
En esta sección se incluyen ejemplos de cómo implementar la inserción sin cookies en el cliente de la aplicación y se incluyen las siguientes subsecciones:
- Instalar y actualizar el SDK de Looker Embed
- Usar el SDK de Looker Embed
- Usar la API
windows.postMessage
de Looker
Instalar o actualizar el SDK de Looker Embed
Para usar la inserción sin cookies, se requieren las siguientes versiones del SDK de Looker:
@looker/embed-sdk >= 2.0.0
@looker/sdk >= 22.16.0
Usar el SDK de Looker Embed
Se ha añadido un nuevo método de inicialización al SDK de inserción para iniciar la sesión sin cookies. Este método acepta dos cadenas de URL o dos funciones de devolución de llamada. Las cadenas de URL deben hacer referencia a los endpoints del servidor de la aplicación insertada. Los detalles de implementación de estos endpoints en el servidor de aplicaciones se describen en la sección Implementación del servidor de aplicaciones de este documento.
getEmbedSDK().initCookieless(
runtimeConfig.lookerHost,
'/acquire-embed-session',
'/generate-embed-tokens'
)
En el siguiente ejemplo se muestra cómo se usan las retrollamadas. Las retrollamadas solo deben usarse cuando la aplicación cliente insertada necesite conocer el estado de la sesión de inserción de Looker. También puede usar el evento session:status
, por lo que no es necesario usar retrollamadas con el SDK Embed.
const acquireEmbedSessionCallback =
async (): Promise<LookerEmbedCookielessSessionData> => {
const resp = await fetch('/acquire-embed-session')
if (!resp.ok) {
console.error('acquire-embed-session failed', { resp })
throw new Error(
`acquire-embed-session failed: ${resp.status} ${resp.statusText}`
)
}
return (await resp.json()) as LookerEmbedCookielessSessionData
}
const generateEmbedTokensCallback =
async ({ api_token, navigation_token }): Promise<LookerEmbedCookielessSessionData> => {
const resp = await fetch('/generate-embed-tokens', {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ api_token, navigation_token }),
})
if (!resp.ok) {
console.error('generate-embed-tokens failed', { resp })
throw new Error(
`generate-embed-tokens failed: ${resp.status} ${resp.statusText}`
)
}
return (await resp.json()) as LookerEmbedCookielessSessionData
}
getEmbedSDK().initCookieless(
runtimeConfig.lookerHost,
acquireEmbedSessionCallback,
generateEmbedTokensCallback
)
Usar la API windows.postMessage
de Looker
Puedes ver un ejemplo detallado de cómo usar la API windows.postMessage
en los archivos message_example.ts
y message_utils.ts
del repositorio Git del SDK Embed. Aquí se detallan los aspectos más destacados del ejemplo.
En el siguiente ejemplo se muestra cómo crear la URL del iframe. La función de retrollamada es idéntica al ejemplo de acquireEmbedSessionCallback
que hemos visto antes.
private async getCookielessLoginUrl(): Promise<string> {
const { authentication_token, navigation_token } =
await this.embedEnvironment.acquireSession()
const url = this.embedUrl.startsWith('/embed')
? this.embedUrl
: `/embed${this.embedUrl}`
const embedUrl = new URL(url, this.frameOrigin)
if (!embedUrl.searchParams.has('embed_domain')) {
embedUrl.searchParams.set('embed_domain', window.location.origin)
}
embedUrl.searchParams.set('embed_navigation_token', navigation_token)
const targetUri = encodeURIComponent(
`${embedUrl.pathname}${embedUrl.search}${embedUrl.hash}`
)
return `${embedUrl.origin}/login/embed/${targetUri}?embed_authentication_token=${authentication_token}`
}
En el siguiente ejemplo se muestra cómo detectar solicitudes de tokens, generar tokens nuevos y enviarlos a Looker. La función de retrollamada es idéntica al ejemplo anterior de generateEmbedTokensCallback
.
this.on(
'session:tokens:request',
this.sessionTokensRequestHandler.bind(this)
)
private connected = false
private async sessionTokensRequestHandler(_data: any) {
const contentWindow = this.getContentWindow()
if (contentWindow) {
if (!this.connected) {
// When not connected the newly acquired tokens can be used.
const sessionTokens = this.embedEnvironment.applicationTokens
if (sessionTokens) {
this.connected = true
this.send('session:tokens', this.embedEnvironment.applicationTokens)
}
} else {
// If connected, the embedded Looker application has decided that
// it needs new tokens. Generate new tokens.
const sessionTokens = await this.embedEnvironment.generateTokens()
this.send('session:tokens', sessionTokens)
}
}
}
send(messageType: string, data: any = {}) {
const contentWindow = this.getContentWindow()
if (contentWindow) {
const message: any = {
type: messageType,
...data,
}
contentWindow.postMessage(JSON.stringify(message), this.frameOrigin)
}
return this
}
Implementación del servidor de aplicaciones
En esta sección se incluyen ejemplos de cómo implementar la inserción sin cookies en el servidor de aplicaciones y se incluyen las siguientes subsecciones:
Implementación básica
La aplicación insertada debe implementar dos endpoints del lado del servidor que invocarán endpoints de Looker. De esta forma, nos aseguramos de que el token de referencia de la sesión siga siendo seguro. Estos son los endpoints:
- Adquirir sesión: si ya existe un token de referencia de sesión y sigue activo, las solicitudes de sesión se unirán a la sesión existente. Se llama a Acquire session cuando se crea un iframe.
- Generar tokens: Looker activa llamadas a este endpoint periódicamente.
Adquirir sesión
En este ejemplo de TypeScript se usa la sesión para guardar o restaurar el token de referencia de la sesión. No es necesario implementar el endpoint en TypeScript.
app.get(
'/acquire-embed-session',
async function (req: Request, res: Response) {
try {
const current_session_reference_token =
req.session && req.session.session_reference_token
const response = await acquireEmbedSession(
req.headers['user-agent']!,
user,
current_session_reference_token
)
const {
authentication_token,
authentication_token_ttl,
navigation_token,
navigation_token_ttl,
session_reference_token,
session_reference_token_ttl,
api_token,
api_token_ttl,
} = response
req.session!.session_reference_token = session_reference_token
res.json({
api_token,
api_token_ttl,
authentication_token,
authentication_token_ttl,
navigation_token,
navigation_token_ttl,
session_reference_token_ttl,
})
} catch (err: any) {
res.status(400).send({ message: err.message })
}
}
)
async function acquireEmbedSession(
userAgent: string,
user: LookerEmbedUser,
session_reference_token: string
) {
await acquireLookerSession()
try {
const request = {
...user,
session_reference_token: session_reference_token,
}
const sdk = new Looker40SDK(lookerSession)
const response = await sdk.ok(
sdk.acquire_embed_cookieless_session(request, {
headers: {
'User-Agent': userAgent,
},
})
)
return response
} catch (error) {
console.error('embed session acquire failed', { error })
throw error
}
}
A partir de Looker 23.8, el dominio de inserción se puede incluir cuando se adquiera la sesión sin cookies. Es una alternativa a la adición del dominio de inserción mediante el panel Administrar > Insertar de Looker. Looker guarda el dominio de inserción en la base de datos interna de Looker, por lo que no se mostrará en el panel Administrar > Insertar. En su lugar, el dominio de inserción se asocia a la sesión sin cookies y solo existe durante la sesión. Consulta las prácticas recomendadas de seguridad si decides aprovechar esta función.
Generar tokens
En este ejemplo de TypeScript se usa la sesión para guardar o restaurar el token de referencia de la sesión. No es necesario implementar el endpoint en TypeScript.
Es importante que sepas cómo gestionar las respuestas 400, que se producen cuando los tokens no son válidos. Aunque no debería producirse una respuesta 400, si ocurre, es recomendable finalizar la sesión de Looker insertado. Puedes finalizar la sesión de inserción de Looker destruyendo el iframe de inserción o asignando el valor cero a session_reference_token_ttl
en el mensaje session:tokens
. Si asignas el valor cero a session_reference_token_ttl
, el iframe de Looker mostrará un cuadro de diálogo de sesión caducada.
No se devuelve una respuesta 400 cuando caduca la sesión de inserción. Si la sesión insertada ha caducado, se devuelve una respuesta 200 con el valor session_reference_token_ttl
establecido en cero.
app.put(
'/generate-embed-tokens',
async function (req: Request, res: Response) {
try {
const session_reference_token = req.session!.session_reference_token
const { api_token, navigation_token } = req.body as any
const tokens = await generateEmbedTokens(
req.headers['user-agent']!,
session_reference_token,
api_token,
navigation_token
)
res.json(tokens)
} catch (err: any) {
res.status(400).send({ message: err.message })
}
}
)
}
async function generateEmbedTokens(
userAgent: string,
session_reference_token: string,
api_token: string,
navigation_token: string
) {
if (!session_reference_token) {
console.error('embed session generate tokens failed')
// missing session reference treat as expired session
return {
session_reference_token_ttl: 0,
}
}
await acquireLookerSession()
try {
const sdk = new Looker40SDK(lookerSession)
const response = await sdk.ok(
sdk.generate_tokens_for_cookieless_session(
{
api_token,
navigation_token,
session_reference_token: session_reference_token || '',
},
{
headers: {
'User-Agent': userAgent,
},
}
)
)
return {
api_token: response.api_token,
api_token_ttl: response.api_token_ttl,
navigation_token: response.navigation_token,
navigation_token_ttl: response.navigation_token_ttl,
session_reference_token_ttl: response.session_reference_token_ttl,
}
} catch (error: any) {
if (error.message?.includes('Invalid input tokens provided')) {
// The Looker UI does not know how to handle bad
// tokens. This shouldn't happen but if it does expire the
// session. If the token is bad there is not much that that
// the Looker UI can do.
return {
session_reference_token_ttl: 0,
}
}
console.error('embed session generate tokens failed', { error })
throw error
}
Consideraciones de implementación
La aplicación insertada debe registrar el token de referencia de la sesión y mantenerlo protegido. Este token debe estar asociado al usuario de la aplicación insertada. El token de la aplicación insertada se puede almacenar de una de las siguientes formas:
- En la sesión del usuario de la aplicación insertada
- En una caché del lado del servidor que está disponible en un entorno agrupado en clústeres
- En una tabla de la base de datos asociada al usuario
Si la sesión se almacena como una cookie, esta debe cifrarse. En el ejemplo del repositorio del SDK de inserción se usa una cookie de sesión para almacenar el token de referencia de la sesión.
Cuando caduque la sesión de inserción de Looker, se mostrará un cuadro de diálogo en el iframe insertado. En este punto, el usuario no podrá hacer nada en la instancia insertada. Cuando esto ocurre, se generan los eventos session:status
, lo que permite que la aplicación insertada detecte el estado actual de la aplicación de Looker insertada y realice alguna acción.
Una aplicación insertada puede detectar si la sesión de inserción ha caducado comprobando si el valor session_reference_token_ttl
que devuelve el endpoint generate_tokens
es cero. Si el valor es cero, significa que la sesión insertada ha caducado. Considera la posibilidad de usar una función de retrollamada para generar tokens cuando se inicialice la inserción sin cookies. La función de retrollamada puede determinar si la sesión insertada ha caducado y destruirá el iframe insertado como alternativa al cuadro de diálogo predeterminado de sesión insertada caducada.
Ejecutar el ejemplo de inserción sin cookies de Looker
El repositorio del SDK de inserción contiene un servidor de nodo Express y un cliente escrito en TypeScript que implementa una aplicación de inserción. Los ejemplos mostrados anteriormente se han tomado de esta implementación. En este artículo se da por hecho que tu instancia de Looker se ha configurado para usar la inserción sin cookies, tal como se ha descrito anteriormente.
Puedes ejecutar el servidor de la siguiente manera:
- Clona el repositorio del SDK de inserción:
git clone git@github.com:looker-open-source/embed-sdk.git
- Cambiar el directorio:
cd embed-sdk
- Instala las dependencias:
npm install
- Configura el servidor tal como se indica en la sección Configurar el servidor de este documento.
- Ejecutar el servidor:
npm run server
Configurar el servidor
Crea un archivo .env
en la raíz del repositorio clonado (se incluye en .gitignore
).
El formato es el siguiente:
LOOKER_WEB_URL=your-looker-instance-url.com
LOOKER_API_URL=https://your-looker-instance-url.com
LOOKER_DEMO_HOST=localhost
LOOKER_DEMO_PORT=8080
LOOKER_EMBED_SECRET=embed-secret-from-embed-admin-page
LOOKER_CLIENT_ID=client-id-from-user-admin-page
LOOKER_CLIENT_SECRET=client-secret-from-user-admin-page
LOOKER_DASHBOARD_ID=id-of-dashboard
LOOKER_LOOK_ID=id-of-look
LOOKER_EXPLORE_ID=id-of-explore
LOOKER_EXTENSION_ID=id-of-extension
LOOKER_VERIFY_SSL=true
LOOKER_REPORT_ID=id-of-report
LOOKER_QUERY_VISUALIZATION_ID=id-of-query-visualization