This guide covers the authentication options available in nanoFramework WebServer.
The WebServer supports three types of authentication that can be applied to controllers and individual methods:
- Basic Authentication - HTTP Basic Auth with username/password
- API Key Authentication - Custom header-based authentication
- None - No authentication required
Authentication can be configured at both the class level (applies to all methods) and method level (overrides class-level settings).
[Authentication("Basic")]
public class SecureController
{
[Route("secure/data")]
public void GetSecureData(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Secure data");
}
}
// Server setup with default credentials
using (WebServer server = new WebServer(80, HttpProtocol.Http, new Type[] { typeof(SecureController) }))
{
server.Credential = new NetworkCredential("admin", "password");
server.Start();
Thread.Sleep(Timeout.Infinite);
}public class UserController
{
[Route("admin")]
[Authentication("Basic:admin secretpassword")]
public void AdminPanel(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Admin panel");
}
[Route("user")]
[Authentication("Basic:user userpass")]
public void UserPanel(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "User panel");
}
}Note: The username cannot contain spaces. Use the format: "Basic:username password"
[Authentication("ApiKey")]
public class ApiController
{
[Route("api/data")]
public void GetData(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "API data");
}
}
// Server setup with default API key
using (WebServer server = new WebServer(80, HttpProtocol.Http, new Type[] { typeof(ApiController) }))
{
server.ApiKey = "MySecretApiKey123";
server.Start();
Thread.Sleep(Timeout.Infinite);
}public class ServiceController
{
[Route("service/premium")]
[Authentication("ApiKey:premium-key-789")]
public void PremiumService(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Premium service");
}
[Route("service/basic")]
[Authentication("ApiKey:basic-key-456")]
public void BasicService(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Basic service");
}
}Clients must include the API key in the request headers:
GET /api/data HTTP/1.1
Host: 192.168.1.100
ApiKey: MySecretApiKey123Note: The header name ApiKey is case-sensitive.
[Authentication("None")]
public class PublicController
{
[Route("public/info")]
public void GetPublicInfo(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Public information");
}
}[Authentication("Basic")] // Default for all methods in this class
public class MixedController
{
[Route("secure/basic")]
public void BasicAuth(WebServerEventArgs e)
{
// Uses class-level Basic authentication
WebServer.OutputAsStream(e.Context.Response, "Basic auth data");
}
[Route("secure/api")]
[Authentication("ApiKey:special-key-123")] // Override with API key
public void ApiKeyAuth(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "API key data");
}
[Route("secure/custom")]
[Authentication("Basic:customuser custompass")] // Override with custom basic auth
public void CustomAuth(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Custom auth data");
}
[Route("public")]
[Authentication("None")] // Override to allow public access
public void PublicAccess(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Public data");
}
}The WebServer supports multiple authentication methods for the same route by creating separate methods:
public class MultiAuthController
{
[Route("data")]
[Authentication("Basic")]
public void DataBasicAuth(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Data via Basic auth");
}
[Route("data")]
[Authentication("ApiKey:key1")]
public void DataApiKey1(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Data via API key 1");
}
[Route("data")]
[Authentication("ApiKey:key2")]
public void DataApiKey2(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Data via API key 2");
}
[Route("data")]
public void DataPublic(WebServerEventArgs e)
{
WebServer.OutputAsStream(e.Context.Response, "Public data");
}
}The server selects the route for a request using this logic:
- No matching methods: Returns 404 (Not Found)
- Authentication provided in request headers:
- Finds methods requiring authentication
- If credentials match: Calls the matching method
- If credentials don't match: Returns 401 (Unauthorized)
- No authentication provided:
- If a public method exists (no auth required): Calls that method
- If only auth-required methods exist: Returns 401 (Unauthorized)
- For Basic auth methods: Includes
WWW-Authenticateheader
using (WebServer server = new WebServer(80, HttpProtocol.Http, new Type[] { typeof(MyController) }))
{
// Set default credentials for Basic authentication
server.Credential = new NetworkCredential("defaultuser", "defaultpass");
// Set default API key
server.ApiKey = "DefaultApiKey123";
server.Start();
Thread.Sleep(Timeout.Infinite);
}- Use HTTPS: Always use HTTPS in production to protect credentials
- Strong Credentials: Use strong passwords and API keys
- Rotate Keys: Regularly rotate API keys and passwords
- Principle of Least Privilege: Grant minimal necessary access
- Secure Storage: Avoid hardcoding credentials in source code
// Load certificate
X509Certificate2 cert = new X509Certificate2(certBytes, privateKeyBytes, "password");
using (WebServer server = new WebServer(443, HttpProtocol.Https, new Type[] { typeof(SecureController) }))
{
server.HttpsCert = cert;
server.SslProtocols = SslProtocols.Tls12;
server.Credential = new NetworkCredential("admin", "securepassword");
server.ApiKey = "SecureApiKey456";
server.Start();
Thread.Sleep(Timeout.Infinite);
}# With default credentials
curl -u admin:password http://192.168.1.100/secure/data
# With custom credentials
curl -u customuser:custompass http://192.168.1.100/secure/custom# With API key
curl -H "ApiKey: MySecretApiKey123" http://192.168.1.100/api/data
# With custom API key
curl -H "ApiKey: special-key-123" http://192.168.1.100/secure/apiHTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="nanoFramework"
Content-Length: 0HTTP/1.1 401 Unauthorized
Content-Length: 0When multiple methods match the same route with conflicting authentication, the server returns 500 with details about the conflicting methods.
- Always 401: Check if method requires authentication and credentials are provided
- Wrong credentials: Verify username/password or API key matches configuration
- Case sensitivity: API key header name is case-sensitive (
ApiKey) - Multiple methods: Ensure no conflicting methods for the same route/auth combination
- Default vs custom: Remember that method-level attributes override class-level ones