Quando Looker è incorporato in un iframe utilizzando l'incorporamento con Single Sign-On (SSO), alcuni browser utilizzano per impostazione predefinita un criterio relativo ai cookie che blocca i cookie di terze parti. I cookie di terze parti vengono rifiutati quando l'iframe incorporato viene caricato da un dominio diverso da quello che carica l'applicazione di incorporamento. In genere, puoi aggirare questo limite richiedendo e utilizzando un dominio vanity. ma non possono essere utilizzati in alcuni scenari. È per questi scenari che è possibile utilizzare l'incorporamento senza cookie di Looker.
Come funziona l'incorporamento senza cookie?
Quando i cookie di terze parti non vengono bloccati, viene creato un cookie di sessione quando un utente accede inizialmente a Looker. Questo cookie viene inviato a ogni richiesta dell'utente e il server Looker lo utilizza per stabilire l'identità dell'utente che ha avviato la richiesta. Quando i cookie sono bloccati, il cookie non viene inviato con una richiesta, pertanto il server Looker non riesce a identificare l'utente associato alla richiesta.
Per risolvere il problema, l'incorporamento senza cookie di Looker associa i token a ogni richiesta che può essere utilizzata per ricreare la sessione utente nel server Looker. È responsabilità dell'applicazione di incorporamento ottenere questi token e renderli disponibili all'istanza di Looker in esecuzione nell'iframe incorporato. Il processo per ottenere e fornire questi token è descritto nel resto del documento.
Per utilizzare entrambe le API, l'applicazione di incorporamento deve essere in grado di autenticarsi nell'API Looker con privilegi amministrativi. Il dominio di incorporamento deve essere elencato anche nella Embed Domain Allowlist (Lista consentita del dominio di incorporamento) oppure, se si utilizza Looker 23.8 o versioni successive, il dominio di incorporamento può essere incluso quando viene acquisita la sessione senza cookie.
Creazione di un iframe di incorporamento di Looker
Il seguente diagramma di sequenza illustra la creazione di un iframe di incorporamento. È possibile generare più iframe contemporaneamente o in un determinato momento futuro. Se implementato correttamente, l'iframe parteciperà automaticamente alla sessione creata dal primo iframe. L'SDK Embed di Looker semplifica questo processo unendo automaticamente la sessione esistente.
- L'utente esegue un'azione nell'applicazione di incorporamento che genera la creazione di un iframe di Looker.
- Il client dell'applicazione di incorporamento acquisisce una sessione di Looker. L'SDK Looker Embed può essere utilizzato per avviare questa sessione, ma è necessario fornire un URL endpoint o una funzione di callback. Se viene utilizzata una funzione di callback, chiamerà il server delle applicazioni di incorporamento per acquisire la sessione di incorporamento Looker. In caso contrario, l'SDK Embed chiama l'URL dell'endpoint fornito.
- Il server delle applicazioni di incorporamento utilizza l'API Looker per acquisire una sessione di incorporamento. Questa chiamata API è simile al processo di firma SSO di Looker, in quanto accetta la definizione dell'utente di incorporamento come input. Se esiste già una sessione di incorporamento Looker per l'utente chiamante, il token di riferimento della sessione associato deve essere incluso nella chiamata. Questo verrà spiegato più dettagliatamente nella sezione Acquisisci sessione di questo documento.
- L'elaborazione dell'endpoint di sessione dell'incorporamento è simile all'endpoint
/login/embed/{signed url)
firmato da SSO, in quanto prevede che la definizione dell'utente di incorporamento di Looker sia il corpo della richiesta, anziché nell'URL. Il processo di acquisizione della sessione di incorporamento convalida e crea o aggiorna l'utente di incorporamento. Può anche accettare un token di riferimento per una sessione esistente. Questo è importante perché consente a più iframe incorporati in Looker di condividere la stessa sessione. L'utente di incorporamento non verrà aggiornato se viene fornito un token di riferimento per la sessione e la sessione non è scaduta. Questo supporta il caso d'uso in cui un iframe viene creato utilizzando un URL SSO firmato e altri iframe vengono creati senza un URL SSO firmato. In questo caso, gli iframe senza URL SSO firmati erediteranno il cookie dalla prima sessione. - La chiamata all'API Looker restituisce quattro token, ciascuno con una durata (TTL):
- Token di autorizzazione (TTL = 30 secondi)
- Token di navigazione (TTL = 10 minuti)
- Token API (TTL = 10 minuti)
- Token di riferimento della sessione (TTL = durata rimanente della sessione)
- Il server delle applicazioni di incorporamento deve tenere traccia dei dati restituiti dai dati di Looker e associarli sia all'utente chiamante sia allo user agent del browser dell'utente chiamante. Nella sezione Genera token di questo documento vengono forniti suggerimenti su come eseguire questa operazione. Questa chiamata restituisce il token di autorizzazione, un token di navigazione, un token API e tutti i TTL associati. Il token di riferimento della sessione deve essere protetto e non esposto nel browser per le chiamate.
Una volta restituiti i token al browser, è necessario creare un URL di accesso incorporato di Looker. L'SDK Embed di Looker creerà automaticamente l'URL di accesso incorporato. Per utilizzare l'API
windows.postMessage
per creare l'URL di accesso incorporato, consulta la sezione Utilizzo dell'APIwindows.postMessage
di Looker di questo documento per consultare alcuni esempi.L'URL di accesso non contiene i dettagli dell'utente incorporato della firma. Contiene l'URI di destinazione, inclusi il token di navigazione e il token di autorizzazione come parametro di ricerca. Il token di autorizzazione deve essere utilizzato entro 30 secondi e può essere utilizzato una sola volta. Se sono necessari iframe aggiuntivi, è necessario acquisire di nuovo una sessione di incorporamento. Tuttavia, se viene fornito il token di riferimento della sessione, il token di autorizzazione verrà associato alla stessa sessione.
L'endpoint di accesso incorporato di Looker determina se l'accesso avviene per l'incorporamento senza cookie, che è indicato dalla presenza del token di autorizzazione. Se il token di autorizzazione è valido, controlla quanto segue:
- La sessione associata è ancora valida.
- L'utente dell'incorporamento associato è ancora valido.
- Lo user agent del browser associato alla richiesta corrisponde allo user agent del browser associato alla sessione.
Se i controlli del passaggio precedente hanno superato, la richiesta viene reindirizzata utilizzando l'URI di destinazione contenuto nell'URL. La procedura è la stessa dell'accesso SSO di Looker Embed.
Questa richiesta è il reindirizzamento all'avvio della dashboard di Looker. Questa richiesta avrà il token di navigazione come parametro.
Prima che l'endpoint venga eseguito, il server Looker cerca il token di navigazione nella richiesta. Se il server trova il token, verifica che:
- La sessione associata è ancora valida.
- Lo user agent del browser associato alla richiesta corrisponde allo user agent del browser associato alla sessione.
Se valida, la sessione viene ripristinata per la richiesta e viene eseguita la richiesta della dashboard.
Il codice HTML per caricare la dashboard viene restituito all'iframe.
La UI di Looker in esecuzione nell'iframe determina che l'HTML della dashboard è una risposta di incorporamento senza cookie. A questo punto, l'interfaccia utente di Looker invia un messaggio all'applicazione di incorporamento che richiede i token recuperati al passaggio 6. L'interfaccia utente resta in attesa finché non riceve i token. Se i token non arrivano, viene visualizzato un messaggio.
L'applicazione di incorporamento invia i token all'iframe incorporato di Looker.
Quando i token vengono ricevuti, l'UI di Looker in esecuzione nell'iframe avvia il processo per il rendering dell'oggetto di richiesta. Durante questo processo, l'UI effettuerà chiamate API al server Looker. Il token API che è stato ricevuto nel passaggio 15 viene inserito automaticamente come intestazione in tutte le richieste API.
Prima di eseguire qualsiasi endpoint, il server Looker cerca il token API nella richiesta. Se il server trova il token, verifica che:
- La sessione associata è ancora valida.
- Lo user agent del browser associato alla richiesta corrisponde allo user agent del browser associato alla sessione.
Se la sessione è valida, viene ripristinata per la richiesta e viene eseguita la richiesta API.
Vengono restituiti i dati della dashboard.
Viene eseguito il rendering della dashboard.
L'utente ha il controllo sulla dashboard.
Generazione di nuovi token
- L'interfaccia utente di Looker in esecuzione nell'iframe incorporato monitora il TTL dei token di incorporamento.
- Quando i token si avvicinano alla scadenza, la UI di Looker invia un messaggio di token di aggiornamento al client dell'applicazione incorporata.
- Il client dell'applicazione di incorporamento richiede nuovi token da un endpoint che sono implementati nel server dell'applicazione di incorporamento. L'SDK Embed di Looker richiede automaticamente i nuovi token, ma è necessario fornire l'URL dell'endpoint o una funzione di callback. Se viene utilizzata la funzione di callback, chiamerà il server di applicazione dell'incorporamento per generare nuovi token. In caso contrario, l'SDK Embed chiama l'URL dell'endpoint fornito.
- L'applicazione di incorporamento trova il
session_reference_token
associato alla sessione di incorporamento. L'esempio fornito nel repository Git dell'SDK Looker Embed utilizza i cookie di sessione, ma può essere usata anche una cache lato server distribuita, ad esempio Redis. - Il server delle applicazioni di incorporamento chiama il server Looker con la richiesta di generare token. Questa richiesta richiede anche API e token di navigazione recenti, oltre allo user agent del browser che ha avviato la richiesta.
- Il server Looker convalida lo user agent, il token di riferimento della sessione, il token di navigazione e il token API. Se la richiesta è valida, vengono generati nuovi token.
- I token vengono restituiti al server dell'applicazione di incorporamento per le chiamate.
- Il server di applicazione di incorporamento rimuove il token di riferimento della sessione dalla risposta e restituisce la risposta rimanente al client dell'applicazione di incorporamento.
- Il client dell'applicazione di incorporamento invia i token appena generati all'interfaccia utente di Looker. L'SDK Embed di Looker esegue questa operazione automaticamente. L'invio dei token è responsabilità dell'incorporamento dei client dell'applicazione che utilizzano l'API
windows.postMessage
. Una volta che l'interfaccia utente di Looker riceve i token, questi verranno utilizzati nelle chiamate API e nelle navigazioni nelle pagine successive.
Implementare l'incorporamento senza cookie di Looker
L'incorporamento senza cookie di Looker può essere implementato utilizzando l'SDK Embed di Looker o l'API windows.postMessage
. Il metodo che utilizza l'SDK Embed di Looker è più facile, ma è disponibile anche un esempio che mostra come utilizzare l'API windows.postMessage
. Spiegazioni dettagliate di entrambe le implementazioni sono disponibili nel file README dell'SDK Embed di Looker. Anche il repository git dell'SDK Incorpora contiene implementazioni funzionanti.
Configurazione dell'istanza di Looker
L'incorporamento senza cookie presenta aspetti in comune con l'incorporamento con Single Sign-On (SSO) di Looker. L'incorporamento senza cookie si basa sull'attivazione dell'autenticazione SSO incorporata. Tuttavia, a differenza del servizio SSO tramite Looker, l'incorporamento senza cookie non utilizza l'impostazione Embed Secret. L'incorporamento senza cookie utilizza un JSON Web Token (JWT) sotto forma di impostazione di incorporamento di JWT Secret, che può essere impostata o reimpostata nella pagina Embed della sezione Platform del menu Admin.
L'impostazione del secret JWT non è obbligatoria, poiché il primo tentativo di creare una sessione di incorporamento senza cookie creerà il JWT. Evita di reimpostare questo token, poiché questa operazione invaliderà tutte le sessioni di incorporamento senza cookie attive.
A differenza del secret di incorporamento, il secret JWT di incorporamento non viene mai esposto, poiché viene utilizzato solo internamente nel server Looker.
Implementazione del client dell'applicazione
Questa sezione include esempi di come implementare l'incorporamento senza cookie nel client dell'applicazione e contiene le seguenti sottosezioni:
- Installare e aggiornare l'SDK Embed di Looker
- Utilizzare l'SDK Embed di Looker
- Utilizzare l'API Looker
windows.postMessage
Installare o aggiornare l'SDK Embed di Looker
Per utilizzare l'incorporamento senza cookie sono necessarie le seguenti versioni dell'SDK Looker:
@looker/embed-sdk >= 1.8
@looker/sdk >= 22.16.0
Utilizzo dell'SDK Embed di Looker
È stato aggiunto un nuovo metodo di inizializzazione all'SDK Embed per avviare la sessione senza cookie. Questo metodo accetta due stringhe URL o due funzioni di callback. Le stringhe URL devono fare riferimento agli endpoint nel server delle applicazioni di incorporamento. I dettagli di implementazione di questi endpoint sul server delle applicazioni sono trattati nella sezione Implementazione dei server delle applicazioni di questo documento.
LookerEmbedSDK.initCookieless(
runtimeConfig.lookerHost,
'/acquire-embed-session',
'/generate-embed-tokens'
)
L'esempio seguente mostra come vengono utilizzati i callback. I callback devono essere utilizzati solo quando è necessario che l'applicazione client di incorporamento sia a conoscenza dello stato della sessione di incorporamento di Looker. Puoi anche utilizzare l'evento session:status
, per cui non è necessario usare i callback con l'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 (): Promise<LookerEmbedCookielessSessionData> => {
const { api_token, navigation_token } = getApplicationTokens() || {}
const resp = await fetch('/generate-embed-tokens', {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ api_token, navigation_token }),
})
if (!resp.ok) {
if (resp.status === 400) {
return { session_reference_token_ttl: 0 }
}
console.error('generate-embed-tokens failed', { resp })
throw new Error(
`generate-embed-tokens failed: ${resp.status} ${resp.statusText}`
)
}
return (await resp.json()) as LookerEmbedCookielessSessionData
}
LookerEmbedSDK.initCookieless(
runtimeConfig.lookerHost,
acquireEmbedSessionCallback,
generateEmbedTokensCallback
)
Utilizzo dell'API windows.postMessage
di Looker
Puoi visualizzare un esempio dettagliato dell'utilizzo dell'API windows.postMessage
nei file message_example.ts
e message_utils.ts
nel repository Git Embed SDK. I punti salienti dell'esempio sono dettagliati qui.
L'esempio seguente mostra come creare l'URL dell'iframe. La funzione di callback è identica all'esempio acquireEmbedSessionCallback
che hai visto in precedenza.
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}`
}
L'esempio seguente mostra come ascoltare le richieste di token, generare nuovi token e inviarli a Looker. La funzione di callback è identica all'esempio generateEmbedTokensCallback
precedente.
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
}
Implementazione del server delle applicazioni
Questa sezione include esempi di come implementare l'incorporamento senza cookie nel server delle applicazioni e contiene le seguenti sottosezioni:
Implementazione di base
L'applicazione di incorporamento è necessaria per implementare due endpoint lato server che richiamano endpoint Looker. Questo serve a garantire che il token di riferimento della sessione rimanga sicuro. Questi sono gli endpoint:
- Acquisisci sessione: se esiste già un token di riferimento per la sessione ed è ancora attivo, le richieste di una sessione verranno incluse nella sessione esistente. La sessione di acquisizione viene chiamata quando viene creato un iframe.
- Genera token: Looker attiva periodicamente le chiamate a questo endpoint.
Acquisisci sessione
Questo esempio in TypeScript utilizza la sessione per salvare o ripristinare il token di riferimento della sessione. Non è necessario implementare l'endpoint in 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 partire da Looker 23.8, il dominio di incorporamento può essere incluso quando viene acquisita la sessione senza cookie. Si tratta di un'alternativa all'aggiunta del dominio di incorporamento utilizzando il riquadro Looker Admin > Embed (Amministrazione > Incorpora). Looker salva il dominio di incorporamento nel database interno di Looker, quindi non verrà mostrato nel riquadro Admin > Embed (Amministrazione > Incorpora). Il dominio di incorporamento è associato alla sessione senza cookie ed esiste solo per la durata della sessione. Leggi le best practice per la sicurezza se decidi di avvalerti di questa funzionalità.
Genera token
Questo esempio in TypeScript utilizza la sessione per salvare o ripristinare il token di riferimento della sessione. Non è necessario implementare l'endpoint in TypeScript.
È importante sapere come gestire le risposte 400, che si verificano quando i token non sono validi. Questo non dovrebbe avvenire, ma nel caso affermativo, è buona norma terminare la sessione.
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')) {
// Currently the Looker UI does not know how to handle bad
// tokens. This should not 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
}
Considerazioni sull'implementazione
L'applicazione di incorporamento deve tenere traccia del token di riferimento della sessione e proteggerlo. Questo token deve essere associato all'utente dell'applicazione incorporata. Il token dell'applicazione di incorporamento può essere archiviato in uno dei seguenti modi:
- Nella sessione dell'utente dell'applicazione incorporata
- In una cache lato server disponibile in un ambiente cluster
- In una tabella di database associata all'utente.
Se la sessione è memorizzata come cookie, il cookie deve essere criptato. L'esempio nel repository di SDK incorporato utilizza un cookie di sessione per memorizzare il token di riferimento della sessione.
Alla scadenza della sessione di incorporamento di Looker, viene visualizzata una finestra di dialogo nell'iframe incorporato. A questo punto, l'utente non potrà fare nulla nell'istanza incorporata. In questo caso verranno generati gli eventi di session:status
, che potranno così rilevare lo stato attuale dell'applicazione Looker incorporata e compiere un'azione.
Esempio di incorporamento senza cookie di Looker
Il repository dell'SDK di incorporamento contiene un semplice client e un nodo Express basati su nodo, scritti in TypeScript e che implementano una semplice applicazione di incorporamento. Gli esempi riportati in precedenza sono tratti da questa implementazione. Quanto segue presuppone che la tua istanza di Looker sia stata configurata per l'utilizzo dell'incorporamento senza cookie come descritto in precedenza.
Puoi eseguire il server come segue:
- Clona il repository dell'SDK Embed -
git clone git@github.com:looker-open-source/embed-sdk.git
- Cambia directory:
cd embed-sdk
- Installa le dipendenze -
npm install
- Configura il server, come mostrato nella sezione Configura il server di questo documento.
- Esegui il server -
npm run server
Configura il server
Crea un file .env
nella directory principale del repository clonato (incluso in .gitignore
).
Il formato è il seguente:
LOOKER_EMBED_HOST=your-looker-instance-url.com.
LOOKER_EMBED_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