Skip to content

delphi-ai/embed-sso-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 

Repository files navigation

Delphi SSO Guide

Introduction

Single Sign-On (SSO) is an authentication method that allows users to access multiple applications with one set of credentials. Delphi's custom SSO implementation lets you streamlines user access to our website embed. This guide will walk you through setting up and using SSO with Delphi.

Important: The security of your SSO implementation relies heavily on keeping your private key confidential. Never share your private key or store it in unsecured locations.

Getting Started

This guide assumes you've already requested and been approved for SSO functionality. Contact support@delphi.ai to begin the process.

1. Generate Your Key Pair

A pair of encryption keys, known as a key-pair, are used to make sure that your application and Delphi's API communicate securely by providing a way to verify that messages sent between one another are coming from a legitimate source.

Our first step is to generate those encryption keys. You have two options for generating your RSA key pair:

Option A: Using OpenSSL (Command Line)

Open your terminal and run the following commands:

openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -pubout -out public_key.pem

This will generate a private_key.pem and a public_key.pem file in your current directory.

Option B: Using Delphi's Built-in Key Pair Generator

  1. Navigate to the SSO settings in your Delphi dashboard.
  2. Click on the "Generate Key Pair" button.
  3. Follow the on-screen instructions to generate and download your key pair.

SSO Settings Key Pair Generator Modal

2. Configure Your SSO Settings

  1. In Delphi's Clone Studio, go to the settings page, and find Single Sign-on (SSO) in the menu bar.
  2. Paste your public key into the designated field and click Save. public key field

3. Implement SSO in Your Application

To implement SSO in your application, you'll need to: General Information Flow Diagram

  1. Generate a JWT (JSON Web Token) signed with your private key.
  2. Send the JWT to the Delphi embed using the following JavaScript code:

Important Note: The only supported SSO field in the JWT payload is email. Any other fields in the JWT will be ignored, except for the standard expiry field. The only supported algorithim is RS256.

To authenticate the Delphi embedding in your app, you can run the following, providing the JWT you generated earlier:

const iframe = document.getElementById('delphi-frame');
const targetOrigin = 'https://embed.delphi.ai'; // Use the embed origin, not the API origin

iframe.contentWindow.postMessage(
  {
    type: 'sso_login',
    token: 'your_generated_jwt_here'
  },
  targetOrigin
);

A sample SSO application is available in sample-sso-app. This demo showcases a basic implementation of the Delphi SSO flow and can serve as a reference for your own implementation.

4. Test Your SSO Implementation

Use Delphi's built-in JWT testing module to verify your token generation:

  1. In Delphi's Clone Studio, go to the settings page, and find Single Sign-on (SSO) in the menu bar.
  2. Navigate to the JWT testing section.
  3. Paste a sample JWT generated by your system.
  4. Click "Test JWT" to verify its validity and contents.

Best Practices

  1. Protect Your Private Key: Store your private key in a secure location, such as a secret management system. Never expose it in client-side code or public repositories. The security of your users depends upon it, and Delphi reserves the right to revoke your SSO approval should these keys be mishandled.

  2. Token Expiration: Set appropriate expiration times for your JWTs to limit the window of opportunity for potential replay attacks. The sample app sets a JWT expiry of 1 hour. The longer the expiration date, the less secure it is.

  3. Robust Iframe Initialization: Many implementations face race conditions when initializing the Delphi embed and handling SSO. Here's what to watch out for:

    Common Pitfalls A typical implementation often looks something like this:

    // ❌ Problematic Implementation
    function initializeDelphiEmbed() {
      const iframe = document.getElementById('delphi-frame');
    
      // This might fail if the scripts inside the iframe haven't loaded yet
      iframe.contentWindow.postMessage(
        {
          type: 'sso_login',
          token: 'your_jwt_token'
        },
        '*' // Never use wildcard in production
      );
    }
    
    // This might run too early
    window.onload = initializeDelphiEmbed;

    This approach can fail because:

    • The iframe might not be fully loaded when the message is sent
    • The Delphi application inside the iframe might not be ready to receive messages
    • There's no retry mechanism if the initialization fails

    Understanding Delphi's Nested Iframe Structure

    Delphi's embed loader creates a nested iframe structure:

    Your Page
    └── Outer Wrapper Iframe (your 'delphi-frame')
        └── Inner App Iframe (contains the actual Delphi app)
    
    • The SSO listener lives in the inner iframe (at https://embed.delphi.ai), not the outer wrapper. If you send the SSO message to the wrong iframe or wrong origin, the browser will silently drop it, causing authentication to fail.

    Recommended Implementation Use a robust sender that handles the nested iframe structure correctly:

    class DelphiSSOSender {
      constructor(iframeId, token, options = {}) {
        this.iframeId = iframeId;
        this.token = token;
        this.maxRetries = options.maxRetries || 5;
        this.retryDelay = options.retryDelay || 500;
        this.loadDelay = options.loadDelay || 100;
        this.iframe = null;
        this.attempts = 0;
      }
    
      async findTargetIframe() {
        return new Promise((resolve, reject) => {
          let tries = 0;
          const maxTries = 20;
    
          const check = () => {
            const container = document.getElementById(this.iframeId);
            const frames = container?.querySelectorAll('iframe') || [];
    
            const embedFrame = Array.from(frames).find((f) => f.src && f.src.includes('embed.delphi.ai')) || frames[frames.length - 1];
    
            if (embedFrame) return resolve(embedFrame);
    
            if (++tries >= maxTries) {
              return reject(new Error(`Embed iframe not found in #${this.iframeId}`));
            }
            setTimeout(check, 100);
          };
          check();
        });
      }
    
      sendToken() {
        if (!this.iframe) return;
    
        const targetOrigin = this.attempts < 2 ? '*' : 'https://embed.delphi.ai';
    
        this.iframe.contentWindow?.postMessage({ type: 'sso_login', token: this.token }, targetOrigin);
    
        console.log(`[DelphiSSO] Token sent (attempt ${this.attempts + 1}) to:`, targetOrigin);
      }
    
      async initialize() {
        this.iframe = await this.findTargetIframe();
    
        this.iframe.addEventListener('load', () => {
          setTimeout(() => this.sendToken(), this.loadDelay);
        });
    
        const doc = this.iframe.contentDocument;
        if (doc?.readyState === 'complete') {
          setTimeout(() => this.sendToken(), this.loadDelay);
        }
    
        const interval = setInterval(() => {
          this.sendToken();
          if (++this.attempts >= this.maxRetries) clearInterval(interval);
        }, this.retryDelay);
      }
    }
    const delphiSSO = new DelphiSSOSender('delphi-frame', 'your_jwt_token');
    delphiSSO.initialize().catch((e) => console.error('[DelphiSSO] Init failed:', e));

    Notes:

    • Nested Iframe Detection: The SSO message must reach the inner embed iframe at https://embed.delphi.ai, not just any iframe on your page. Always derive the targetOrigin from the iframe's src attribute, not from the API host.
    • Cross-Origin Limitations: Do not read iframe.contentWindow.location or iframe.contentWindow.document on cross‑origin iframes; browsers block this by design due to Same-Origin Policy.
    • Specific Origins Only: Always use a specific targetOrigin (e.g., https://embed.delphi.ai), never a wildcard (*) in production.
  4. Same-Origin Policy Compliance: Never attempt to read iframe.contentWindow.location or iframe.contentWindow.document from a cross-origin Delphi embed, as browsers will block these operations for security reasons. Use postMessage with a specific targetOrigin for cross-origin communication.

By following these guidelines and best practices, you'll ensure a secure and efficient SSO implementation.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •