Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ A security-first Blazor iframe component with automatic resizing, cross-frame me

- **Security-First Design** - Built-in origin validation, message filtering, and sandbox isolation
- **Content Security Policy** - Comprehensive CSP integration with fluent configuration API
- **Cross-Frame Messaging** - Secure postMessage communication with validation
- **Bidirectional Communication** - Secure postMessage communication with validation for both directions
- **Sandbox Support** - Multiple security levels from permissive to paranoid isolation
- **Environment-Aware** - Different configurations for development vs production
- **Automatic Resizing** - Smart height adjustment based on iframe content
Expand Down Expand Up @@ -57,13 +57,18 @@ dotnet add package BlazorFrame
<!-- Simple iframe with automatic security -->
<BlazorFrame Src="https://example.com" />

<!-- Production-ready configuration -->
<BlazorFrame Src="https://widget.example.com"
<!-- Production-ready configuration with bidirectional communication -->
<BlazorFrame @ref="iframeRef"
Src="https://widget.example.com"
SecurityOptions="@securityOptions"
OnValidatedMessage="HandleMessage"
OnSecurityViolation="HandleViolation" />

<button @onclick="SendDataToIframe">Send Data</button>

@code {
private BlazorFrame? iframeRef;

private readonly MessageSecurityOptions securityOptions = new MessageSecurityOptions()
.ForProduction() // Strict security settings
.WithBasicSandbox() // Enable iframe sandboxing
Expand All @@ -79,7 +84,15 @@ dotnet add package BlazorFrame
{
Console.WriteLine($"Security violation: {violation.ValidationError}");
return Task.CompletedTask;
};
}

private async Task SendDataToIframe()
{
if (iframeRef != null)
{
await iframeRef.SendTypedMessageAsync("user-data", new { userId = 123, name = "John" });
}
}
}
```

Expand Down
219 changes: 64 additions & 155 deletions docs/configuration/communication-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,87 @@

**Cross-frame messaging and event handling for BlazorFrame**

This guide covers all aspects of configuring communication between your Blazor application and iframe content, including message validation, origin control, and event handling.
This guide covers all aspects of configuring communication between your Blazor application and iframe content, including message validation, origin control, event handling, and **bidirectional communication**.

## Message Handling Overview

BlazorFrame provides two main approaches to handling messages:
BlazorFrame provides comprehensive communication capabilities:

- **Validated Messages** (`OnValidatedMessage`) - Recommended for new implementations
- **Iframe -> Host** (`OnValidatedMessage`) - Receive messages from iframe with validation
- **Host -> Iframe** (`SendMessageAsync`) - Send messages to iframe with security validation
- **Raw Messages** (`OnMessage`) - Legacy support for simple scenarios

## Basic Message Configuration

### Essential Message Handling
### Bidirectional Communication

```razor
<BlazorFrame Src="@widgetUrl"
SecurityOptions="@messageOptions"
OnValidatedMessage="HandleValidatedMessage"
OnSecurityViolation="HandleSecurityViolation" />
<BlazorFrame @ref="iframeRef"
Src="@widgetUrl"
OnValidatedMessage="HandleMessage" />

<button class="btn btn-primary" @onclick="SendDataToIframe">
Send Data to Iframe
</button>

@code {
private readonly MessageSecurityOptions messageOptions = new MessageSecurityOptions()
.ForProduction()
.WithBasicSandbox();
private BlazorFrame? iframeRef;

private async Task HandleValidatedMessage(IframeMessage message)
// Send structured data to iframe
private async Task SendDataToIframe()
{
Logger.LogInformation("Received message from {Origin}: {Data}",
message.Origin, message.Data);

// Process the validated message
await ProcessMessage(message);
if (iframeRef == null) return;

var success = await iframeRef.SendMessageAsync(new
{
type = "data-update",
timestamp = DateTime.UtcNow,
data = new
{
userId = currentUser.Id,
preferences = currentUser.Preferences,
theme = currentTheme
}
});

if (success)
{
Logger.LogInformation("Data sent successfully to iframe");
}
}

private async Task HandleSecurityViolation(IframeMessage violation)
// Send typed messages with automatic structure
private async Task SendNotification()
{
Logger.LogWarning("Security violation: {Error}", violation.ValidationError);

// Handle security issues
await HandleSecurityIssue(violation);
await iframeRef.SendTypedMessageAsync("notification", new
{
message = "Hello from host!",
level = "info",
timestamp = DateTimeOffset.UtcNow
});
}

private async Task HandleMessage(IframeMessage message)
{
if (message.MessageType == "request-user-data")
{
// Respond to iframe's request for user data
await SendUserDataToIframe();
}
}

private async Task SendUserDataToIframe()
{
await iframeRef.SendTypedMessageAsync("user-data-response", new
{
user = new
{
id = currentUser.Id,
name = currentUser.Name,
email = currentUser.Email,
permissions = currentUser.Permissions
}
});
}
}
```
Expand Down Expand Up @@ -336,139 +378,6 @@ public class MessageProcessor
}
```

## Bidirectional Communication

### Sending Messages to Iframe

```razor
<BlazorFrame @ref="iframeRef"
Src="@widgetUrl"
OnValidatedMessage="HandleMessage" />

<button class="btn btn-primary" @onclick="SendDataToIframe">
Send Data to Iframe
</button>

@code {
private BlazorFrame? iframeRef;

private async Task SendDataToIframe()
{
if (iframeRef == null) return;

var messageData = new
{
type = "data-update",
timestamp = DateTime.UtcNow,
data = new
{
userId = currentUser.Id,
preferences = currentUser.Preferences,
theme = currentTheme
}
};

await iframeRef.SendMessageAsync(messageData);
}

private async Task HandleMessage(IframeMessage message)
{
if (message.MessageType == "request-user-data")
{
// Respond to iframe's request for user data
await SendUserDataToIframe();
}
}

private async Task SendUserDataToIframe()
{
var userData = new
{
type = "user-data-response",
user = new
{
id = currentUser.Id,
name = currentUser.Name,
email = currentUser.Email,
permissions = currentUser.Permissions
}
};

await iframeRef.SendMessageAsync(userData);
}
}
```

### Request-Response Pattern

```razor
@code {
private readonly Dictionary<string, TaskCompletionSource<object>> pendingRequests = new();

private async Task<T> SendRequestToIframe<T>(string requestType, object data)
{
var requestId = Guid.NewGuid().ToString();
var tcs = new TaskCompletionSource<object>();

pendingRequests[requestId] = tcs;

try
{
// Send request to iframe
await iframeRef.SendMessageAsync(new
{
type = requestType,
requestId = requestId,
data = data
});

// Wait for response with timeout
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
cts.Token.Register(() => tcs.TrySetCanceled());

var response = await tcs.Task;
return JsonSerializer.Deserialize<T>(response.ToString());
}
finally
{
pendingRequests.Remove(requestId);
}
}

private async Task HandleMessage(IframeMessage message)
{
// Handle responses to our requests
if (message.MessageType?.EndsWith("-response") == true)
{
var responseData = JsonSerializer.Deserialize<ResponseMessage>(message.Data);

if (pendingRequests.TryGetValue(responseData.RequestId, out var tcs))
{
tcs.SetResult(responseData.Data);
}
}
}

// Usage example
private async Task GetIframeData()
{
try
{
var iframeData = await SendRequestToIframe<IframeDataResponse>(
"get-data",
new { category = "user-preferences" }
);

Logger.LogInformation("Received iframe data: {Data}", iframeData);
}
catch (OperationCanceledException)
{
Logger.LogWarning("Request to iframe timed out");
}
}
}
```

## Event Handling

### Comprehensive Event Configuration
Expand Down
33 changes: 33 additions & 0 deletions docs/core-features/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,39 @@ Complete reference for all BlazorFrame component parameters, their types, defaul
}
```

### OnMessageSent
**Type:** `EventCallback<string>`
**Description:** Fired when a message is successfully sent to the iframe.

```razor
<BlazorFrame Src="https://example.com" OnMessageSent="HandleMessageSent" />

@code {
private Task HandleMessageSent(string messageJson)
{
Logger.LogDebug("Message sent successfully: {Message}", messageJson);
return Task.CompletedTask;
}
}
```

### OnMessageSendFailed
**Type:** `EventCallback<Exception>`
**Description:** Fired when sending a message to the iframe fails.

```razor
<BlazorFrame Src="https://example.com" OnMessageSendFailed="HandleSendFailure" />

@code {
private Task HandleSendFailure(Exception ex)
{
Logger.LogError(ex, "Failed to send message to iframe");
// Handle the failure appropriately
ShowErrorToUser("Communication with widget failed");
return Task.CompletedTask;
}
}
```
## Styling Parameters

### AdditionalAttributes
Expand Down
6 changes: 3 additions & 3 deletions src/BlazorFrame/BlazorFrame.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>BlazorFrame</PackageId>
<Version>2.1.2</Version>
<Description>A enhanced secure Blazor iFrame component with built-in origin validation and message security.</Description>
<Version>2.2.0</Version>
<Description>A enhanced secure Blazor iFrame component with built-in origin validation, bidirectional messaging, and comprehensive security features.</Description>
<PackageProjectUrl>https://www.github.com/Tim-Maes/BlazorFrame</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://www.github.com/Tim-Maes/BlazorFrame</RepositoryUrl>
<PackageTags>blazor; iframe; wasm; security; postmessage; origin-validation;</PackageTags>
<PackageTags>blazor; iframe; wasm; security; postmessage; origin-validation; bidirectional;</PackageTags>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<ApplicationIcon>BlazorFrameIcon.ico</ApplicationIcon>
</PropertyGroup>
Expand Down
Loading
Loading