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+.
1. Looker Embed SDK (Recommended)
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):
HOST+/login/embed/EMBED_URL(e.g.,/embed/dashboards/1)nonce(JSON string)time(Integer)session_length(Integer)external_user_id(JSON string)permissions(JSON array)models(JSON array)group_ids(JSON array)external_group_id(JSON string)user_attributes(JSON object)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.
- Acquire Session: Calls
acquire_embed_cookieless_sessionAPI. Returns tokens (exceptsession_reference_tokenwhich must be kept server-side). - Generate Tokens: Calls
generate_tokens_for_cookieless_sessionwhen 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:
EmbedSsoParamsmight not acceptaccess_filtersas 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_datapermission and access to the specifiedmodels. - SDK Logging: Enable in browser console with
localStorage.debug = 'looker:chatty:*'.