Skip to main content

Looker SSO Embed Skill

This skill allows agents to assist users in setting up, implementing, and troubleshooting Looker SSO (signed) and Cookieless embedding using the Looker Embed SDK.

Overview

Looker embedding allows users to access private Looker content (Dashboards, Looks, Explores, Reports, etc.) within an external application.

  • SSO (Signed) Embedding: Authenticates users via a cryptographically signed URL generated by the parent application.
  • Cookieless Embedding: Uses short-lived tokens to mitigate third-party cookie blocking, available in Looker 22.20+.

The Looker JavaScript Embed SDK is the preferred way to implement embedding.

Installation & Initialization

import { getEmbedSDK } from '@looker/embed-sdk'

// Initialize for SSO
getEmbedSDK().init('looker.example.com', '/auth')

// Initialize for Cookieless
getEmbedSDK().initCookieless(
'looker.example.com',
'/acquire-embed-session',
'/generate-embed-tokens'
)

Basic Implementation (SDK 2.0.0+)

SDK 2.0.0 introduces a unified connection that allows navigating between content types without recreating the iframe.

const connection = await getEmbedSDK()
.createDashboardWithId('42')
.appendTo('#embed-container')
.on('dashboard:run:start', () => console.log('Running...'))
.withDynamicIFrameHeight() // Automatically adjust height
.build()
.connect()

// Interacting with the connection
if (connection.getPageType() === 'dashboards') {
connection.asDashboardConnection().run()
}

// Navigating without iframe recreation
connection.loadDashboard('43')
connection.loadUrl('/embed/dashboards/44?state=California')

2. Signed URL Construction (The /auth Endpoint)

The backend /auth endpoint must generate a signed URL using the Looker Embed Secret.

Signature String Order (Concatenate with \n):

  1. HOST + /login/embed/
  2. EMBED_URL (e.g., /embed/dashboards/1)
  3. nonce (JSON string)
  4. time (Integer)
  5. session_length (Integer)
  6. external_user_id (JSON string)
  7. permissions (JSON array)
  8. models (JSON array)
  9. group_ids (JSON array)
  10. external_group_id (JSON string)
  11. user_attributes (JSON object)
  12. access_filters (JSON object - {} if unused)

Example Python Utility (server_utils/auth_utils.py):

import hmac, hashlib, json, base64, time, binascii

def create_signed_url(src, user, host, secret):
json_time = json.dumps(int(time.time()))
json_nonce = json.dumps(binascii.hexlify(os.urandom(16)).decode())

path = '/login/embed/' + quote(src)

# Values must be in EXACT order
string_to_sign = "\n".join([
host + "/login/embed/",
path,
json_nonce,
json_time,
json.dumps(user['session_length']),
json.dumps(user['external_user_id']),
json.dumps(user['permissions']),
json.dumps(user['models']),
json.dumps(user.get('group_ids', [])),
json.dumps(user.get('external_group_id', "")),
json.dumps(user.get('user_attributes', {})),
json.dumps(user.get('access_filters', {}))
])

signer = hmac.new(secret.encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha1)
signature = base64.b64encode(signer.digest()).decode('utf-8')
# Final URL construction includes all params + signature

2.1 Alternate Approach: Using Looker API (create_sso_embed_url)

Instead of manually constructing and signing the URL, you can use the Looker API endpoint create_sso_embed_url via the Looker Python SDK. This avoids manual signature generation errors.

Example Python Implementation:

import looker_sdk
from looker_sdk import models40 as models

Initialize SDK (ensure LOOKERSDK_BASE_URL, LOOKERSDK_CLIENT_ID, LOOKERSDK_CLIENT_SECRET are set)

sdk = looker_sdk.init40()

def get_embed_url(src: str): base_url = os.environ.get("LOOKER_BASE_URL") target_url = f"{base_url}{src}"

try: response = sdk.create_sso_embed_url( body=models.EmbedSsoParams( target_url=target_url, session_length=3600, external_user_id="user_1", permissions=["access_data", "see_user_dashboards", "see_lookml_dashboards"], models=["basic_ecomm"], group_ids=["22"], external_group_id="", user_attributes={},

Note: access_filters might not be supported as a keyword argument in some SDK versions

force_logout_login=False, ) ) return response.url except Exception as e: print(f"Error: {e}") return None

3. Cookieless Embedding

Cookieless embedding requires two backend endpoints to manage sessions and tokens.

  1. Acquire Session: Calls acquire_embed_cookieless_session API. Returns tokens (except session_reference_token which must be kept server-side).
  2. Generate Tokens: Calls generate_tokens_for_cookieless_session when existing tokens are near expiration.

4. Advanced SDK Features

  • Dynamic Height: .withDynamicIFrameHeight() handles iframe resizing based on content.
  • Full Screen: .withAllowAttr('fullscreen') enables browser fullscreen mode for tiles.
  • Dialog Scroll: .withDialogScroll() ensures Looker dialogs (like drills) are positioned correctly relative to the host scroll.
  • Merged Query Flow: .withMergedQueryEditFlow() allows the host app to intercept and handle merged query editing.

5. Troubleshooting

  • Invalid Signature: Most common issue. Verify parameter order, JSON stringification (especially empty objects/arrays), and encoding.
  • SDK Model Mismatch: EmbedSsoParams might not accept access_filters as a keyword argument in some SDK versions (e.g., EmbedSsoParams.__init__() got an unexpected keyword argument 'access_filters'), even if documented. Try omitting it if it's empty.
  • Signature Quirks: In manual signing, the string /login/embed/ may need to appear in BOTH line 1 (Host + /login/embed/) and line 2 (Path) of the string to sign, depending on the example followed.
  • Third-Party Cookies: If using SSO without vanity domains, browsers like Safari will block cookies. Use Cookieless Embed or Vanity Domains to resolve.
  • Domain Allowlist: Ensure the host domain is in Admin > Embed > Embedded Domain Allowlist.
  • Whale/401 Errors: Check if the Looker user has the access_data permission and access to the specified models.
  • SDK Logging: Enable in browser console with localStorage.debug = 'looker:chatty:*'.