Skip to content

OIDC Check Session Iframe — Cross-Application Session State Monitoring

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about OIDC Check Session Iframe. We cover key concepts, practical examples, and best practices to help you master this topic.

The check_session_iframe is a hidden iframe hosted by the OIDC provider that client applications use to query the current session state via postMessage, enabling real-time session monitoring across multiple applications.

What You'll Learn

  • How the check_session_iframe enables cross-application session monitoring
  • How to implement postMessage-based session state queries
  • How to handle session state changes in your application

Why It Matters

When a user logs out of one application or the provider terminates their session, other applications need to know immediately. Without the check_session_iframe, each app would need its own polling mechanism against the provider's API, creating load and complexity.

Real-World Use

DodaMail, DodaDrive, and DodaCalendar all embed the same check_session_iframe from the provider. When the user's session expires, all three applications detect the change within 30 seconds and gracefully redirect to the login page without the user seeing stale data.

flowchart LR
    subgraph Browser
        I["Check Session Iframe\nhttps://accounts.example.com/check_session"]
        A1["DodaMail\nApp Iframe"]
        A2["DodaDrive\nApp Iframe"]
    end
    I <-->|"postMessage"| A1
    I <-->|"postMessage"| A2
    I -->|"Client ID + Session State"| P["Provider Session\nRegistry"]
    P -->|"Changed / Unchanged"| I
    style I fill:#dbeafe,stroke:#2563eb
    style P fill:#fef3c7,stroke:#d97706

Loading the Iframe

<!-- Load the check session iframe (hidden) -->
<iframe id="op-check-session-iframe"
  src="https://accounts.example.com/check_session"
  style="display: none; position: absolute; width: 0; height: 0;"
  sandbox="allow-scripts allow-same-origin">
</iframe>

Implementing Session Monitoring

class SessionMonitor {
  constructor(providerOrigin, clientId, checkIntervalMs = 30000) {
    this.providerOrigin = providerOrigin;
    this.clientId = clientId;
    this.checkIntervalMs = checkIntervalMs;
    this.sessionState = sessionStorage.getItem('oidc_session_state');
    this.timer = null;

    window.addEventListener('message', this.handleMessage.bind(this));
  }

  start() {
    if (!this.sessionState) {
      console.warn('No session state to monitor');
      return;
    }
    // Initial check after iframe loads
    setTimeout(() => this.check(), 2000);
    // Periodic check
    this.timer = setInterval(() => this.check(), this.checkIntervalMs);
  }

  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }

  check() {
    const iframe = document.getElementById('op-check-session-iframe');
    if (!iframe || !iframe.contentWindow) return;

    const message = `${this.clientId} ${this.sessionState}`;
    iframe.contentWindow.postMessage(message, this.providerOrigin);
  }

  handleMessage(event) {
    // CRITICAL: Validate message origin
    if (event.origin !== this.providerOrigin) {
      console.warn('Ignoring message from untrusted origin:', event.origin);
      return;
    }

    if (event.data === 'unchanged') {
      console.log('Session still active');
      this.dispatchEvent('session-active');
    } else if (event.data === 'changed') {
      console.log('Session state changed or ended');
      this.sessionState = null;
      sessionStorage.removeItem('oidc_session_state');
      this.dispatchEvent('session-ended');
      this.stop();
    }
  }

  dispatchEvent(name) {
    document.dispatchEvent(new CustomEvent(name));
  }
}

// Usage
const monitor = new SessionMonitor(
  'https://accounts.example.com',
  'doda-browser'
);

document.addEventListener('session-ended', () => {
  // Redirect to login or show overlay
  window.location.href = '/login?reason=session_expired';
});

monitor.start();

Server-Side Validation

The check_session_iframe result should be validated server-side before performing sensitive operations:

from flask import Flask, session, jsonify, request
import requests

app = Flask(__name__)

@app.route('/api/sensitive-action')
def sensitive_action():
    access_token = session.get('access_token')
    if not access_token:
        return jsonify({"error": "not_authenticated"}), 401

    # Server-side session verification
    introspect_response = requests.post(
        "https://accounts.example.com/introspect",
        data={"token": access_token},
        auth=("doda-browser", "client-secret")
    )
    token_data = introspect_response.json()

    if not token_data.get('active'):
        return jsonify({
            "error": "session_expired",
            "message": "Your session has expired. Please log in again."
        }), 401

    return jsonify({"data": "sensitive data here"})

Common Mistakes

1. Not Validating postMessage Origin

Any website can send messages to your window. Always validate event.origin matches the provider's origin to prevent spoofing.

2. Checking Too Frequently

Every check sends a postMessage and potentially queries the provider's session store. 30-second intervals balance responsiveness with load.

3. Relying Only on Client-Side Check

The check_session_iframe result is not cryptographically signed. Use server-side token introspection for security-critical decisions.

4. Not Handling Iframe Load Failures

If the iframe fails to load (blocked, network error), the monitor silently breaks. Add error handling and fallback polling.

5. Forgetting to Stop Monitoring on Logout

When the user logs out, stop the monitoring timer to prevent errors from the now-invalid session state.

Practice Questions

  1. What transport mechanism does check_session_iframe use?
  2. Why must you validate event.origin in the message handler?
  3. What are the two possible responses from the iframe?
  4. How often should you poll the iframe for changes?
  5. What should you do when the session state changes to changed?

Answers

  1. HTML5 postMessage between Windows/iframes. 2. To prevent spoofed messages from untrusted origins. 3. unchanged (session still active) or changed (session ended). 4. Every 30 seconds is a good balance. 5. Redirect to login, clear local session, and stop monitoring.

Challenge

Build a session monitor that uses the check_session_iframe for four concurrent OIDC applications, displays their session states in a dashboard, and triggers a coordinated redirect to a single login page when any session ends.

FAQ

What is the check_session_iframe?

A hidden iframe hosted by the OIDC provider for cross-application session state monitoring via postMessage.

How does the check_session_iframe communicate?

The client iframe sends postMessage with client ID and session state; the provider responds with changed or unchanged.

Is the check_session_iframe secure?

It uses origin validation via postMessage, but the result should be verified server-side for sensitive operations.

What happens if the iframe is blocked?

The session monitor fails silently. Implement a fallback that directly calls the provider's introspection endpoint.

Can I have multiple apps monitoring the same iframe?

Yes. Each app embeds the same iframe and sends its own postMessage queries with its client ID and session state.

Mini Project

Build a session monitoring dashboard that embeds a provider's check_session_iframe, monitors session states for three simulated applications, displays real-time status indicators, logs state changes with timestamps, and provides a manual session refresh button.

What's Next

  • Learn about front-channel logout for browser-based session termination
  • Explore back-channel logout for server-to-server logout notifications
  • Continue to post-logout redirect URIs for user experience after logout

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro