Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>GwpMinimalApiSample</AssemblyName>
<RootNamespace>Genetec.Dap.CodeSamples</RootNamespace>
<Description>Sample project</Description>
<Company>Genetec Inc.</Company>
<Copyright>Copyright © Genetec Inc. 2025</Copyright>
</PropertyGroup>

</Project>
62 changes: 62 additions & 0 deletions Samples/Genetec Web Player/GwpMinimalApiSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2025 Genetec Inc.
// Licensed under the Apache License, Version 2.0

using System.Net.Http.Headers;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

var mediaGateway = builder.Configuration.GetSection("MediaGateway");
var endpoint = mediaGateway["Endpoint"]?.TrimEnd('/') ?? throw new InvalidOperationException("MediaGateway:Endpoint is required.");
var username = mediaGateway["Username"] ?? throw new InvalidOperationException("MediaGateway:Username is required.");
var password = mediaGateway["Password"] ?? string.Empty;
var sdkCertificate = mediaGateway["SdkCertificate"] ?? throw new InvalidOperationException("MediaGateway:SdkCertificate is required.");
var serverVersion = mediaGateway["ServerVersion"];

var authorizationParameter = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username};{sdkCertificate}:{password}"));

builder.Services.AddHttpClient("MediaGateway", client =>
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationParameter);
}).ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();
if (builder.Environment.IsDevelopment())
{
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
}
return handler;
});

var app = builder.Build();

app.UseStaticFiles();

app.MapGet("/api/config", () => Results.Ok(new
{
mediaGatewayEndpoint = endpoint,
serverVersion = string.IsNullOrWhiteSpace(serverVersion) ? null : serverVersion.Trim(),
}));

app.MapGet("/api/token/{cameraId}", async (string cameraId, IHttpClientFactory httpClientFactory) =>
{
if (string.IsNullOrWhiteSpace(cameraId))
{
return Results.BadRequest("A camera GUID is required.");
}

var client = httpClientFactory.CreateClient("MediaGateway");
var response = await client.GetAsync($"{endpoint}/v2/token/{Uri.EscapeDataString(cameraId.Trim())}");

if (!response.IsSuccessStatusCode)
{
return Results.StatusCode((int)response.StatusCode);
}

var token = await response.Content.ReadAsStringAsync();
return Results.Text(token);
});

app.MapFallbackToFile("index.html");

app.Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"GwpMinimalApiSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:59205;http://localhost:59206"
}
}
}
77 changes: 77 additions & 0 deletions Samples/Genetec Web Player/GwpMinimalApiSample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# GWP Minimal API Sample

This sample demonstrates hosting the Genetec Web Player inside an ASP.NET Core Minimal API application:

- ASP.NET Core serves a static HTML page that loads and runs GWP.
- A server-side `/api/token/{cameraId}` endpoint proxies token requests to the Media Gateway using credentials from `appsettings.json`. Media Gateway credentials never reach the browser.
- A `/api/config` endpoint provides the Media Gateway endpoint and server version to the page without exposing authentication details.
- The page loads `gwp.js` directly from the target Media Gateway and uses the browser environment GWP expects.

## Run

```powershell
dotnet run
```

Then open the URL shown in the console output (for example, `https://localhost:5001`).

### Configuration

Media Gateway settings are configured in `appsettings.json`:

```json
{
"MediaGateway": {
"Endpoint": "https://localhost/media",
"Username": "admin",
"Password": "",
"SdkCertificate": "KxsD11z743Hf5Gq9mv3+5ekxzemlCiUXkTFY5ba1NOGcLCmGstt2n0zYE9NsNimv",
"ServerVersion": ""
}
}
```

You can also use environment variables or user secrets. If you haven't already initialized user secrets for this project, run:

```powershell
dotnet user-secrets init
dotnet user-secrets set "MediaGateway:Password" "your-password"
```

## Required environment setup

### 1. Trust the Media Gateway certificate

If the Media Gateway certificate is self-signed or otherwise untrusted, the browser will fail to load `gwp.js` or connect to the gateway.

For development, the sample automatically allows certificate warnings when connecting to the Media Gateway from the server-side token endpoint. The browser must still trust the certificate for the `gwp.js` script load and WebSocket connections. Add the certificate to the browser's trust store or use a trusted certificate.

### 2. Allow the page origin in Media Gateway CORS

If strict CORS is enabled, add the ASP.NET application origin to `MediaGateway.gconfig`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<MediaGateway EnforceStrictCrossOrigin="true">
<AllowedOrigin Origin="https://localhost:5001" />
</MediaGateway>
Comment thread
alafleur-genetec marked this conversation as resolved.
</Configuration>
```

Restart the Media Gateway role after the change.

### 3. Use a matching GWP build

The sample loads `gwp.js` from `${mediaGatewayEndpoint}/v2/files/gwp.js` so the player version matches the Security Center version.

## Scope and limitations

- This sample demonstrates a feasible hosting pattern. It is not a production-ready security design.
- This sample has no user authentication. Anyone who can reach the application can view camera streams. A real application must add its own authentication layer to control who can access the application.
- Media Gateway credentials are stored in `appsettings.json`. For production, use a secure configuration provider such as user secrets, Azure Key Vault, or environment variables.
- The default SDK certificate is the Genetec development certificate intended for SDK development only.
- A Content Security Policy meta tag restricts script sources, connections, and media to `self`, `https:`, `wss:`, and `blob:`. The policy allows `'unsafe-inline'` for scripts and styles because the page uses inline markup. The Razor Pages sample demonstrates how to use CSP nonces instead.
- Player startup is cancellable. Clicking Stop during script load or session establishment cancels the in-flight start and cleans up any partially created player.
- Browser autoplay rules apply to audio.
- Video rendering and overlays remain in the HTML layer, not the ASP.NET server.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"MediaGateway": {
"Endpoint": "https://localhost/media",
"Username": "admin",
"Password": "",
"SdkCertificate": "KxsD11z743Hf5Gq9mv3+5ekxzemlCiUXkTFY5ba1NOGcLCmGstt2n0zYE9NsNimv",
"ServerVersion": ""
}
}
Loading