This page explains how to:
Create YOUR OWN proxy server that forwards requests to your Databrain backend
Configure the proxy to validate requests and add authentication headers
Your proxy acts as a secure gateway between your frontend and the Databrain backend.
Proxy authentication lets you host a secure intermediary server that validates incoming requests and forwards them to your Databrain backend. This approach keeps your plugin tokens secure on your server, never exposing them to the client.
Enhanced Security: With proxy authentication, your frontend never handles plugin tokens. Your proxy validates requests and adds the required authentication before forwarding to Databrain.
How Proxy Authentication Works
Client Sends Request
Your frontend sends a request to your proxy server with the X-Proxy-Auth-Key header for validation.
Proxy Validates Request
Your proxy validates the X-Proxy-Auth-Key header. If invalid or missing, return an error response.
Proxy Forwards Request
Your proxy constructs the upstream URL, adds the X-Plugin-Token header, and forwards the request to your Databrain backend exactly as received.
Response Returned
Your proxy returns the Databrain backend response to the client exactly as received (status code, body, and headers).
What Your Proxy Must Do
Your proxy can be built on any backend : Node.js, Express, FastAPI, Python, Go, Lambda, Cloudflare Worker, etc. It must receive requests from your frontend and forward them to your Databrain backend exactly as-is, with required headers added.
1. Read the Incoming Request
Capture all parts of the incoming request:
Path
Query parameters
HTTP method
Headers
Body (JSON, text, or binary/raw)
Body must not be modified. Preserve the raw content if it’s binary (zip, pdf, excel, etc.) or text/JSON for other API calls.
2. Validate Proxy Authentication
Your proxy must read and validate the X-Proxy-Auth-Key header:
X-Proxy-Auth-Key: <your-secret-key>
Compare with your stored secret value. If missing or invalid, return an error:
{
"error" : { "message" : "missing or invalid proxy key" }
}
Return appropriate HTTP status (401 or 403).
3. Forward Request to Databrain Backend
Construct Upstream URL
<SELFHOSTED_BACKEND_URL>/<path>?<query>
Forward Original Method
Forward the exact HTTP method: GET, POST, PUT, PATCH, DELETE.
Forward Body Exactly
Do not modify the request payload in any way.
Send these headers to your Databrain backend:
Header Description AcceptForward from incoming request Accept-LanguageForward from incoming request User-AgentForward from incoming request Content-TypeForward if present in incoming request X-Plugin-OriginForward if provided by frontend X-Proxy-Auth-KeyForward original value X-Proxy-Auth-UrlForward original value X-Plugin-TokenYour plugin token (added by proxy) OriginForward original value
4. Return Response Exactly
Return exactly what the Databrain backend returns:
Status code
Response body (binary or text, do not transform)
Response headers
Binary responses (zip, pdf, xlsx) must be forwarded as-is. Do not convert binary content to text.
About X-Plugin-Token
The X-Plugin-Token header authenticates your requests with the Databrain backend. You can provide this token in two ways:
A saved token - Use a pre-generated guest token stored in your environment
Generate on demand - Call the Databrain Guest Token API to generate tokens dynamically
Every error your proxy returns should follow this format:
{
"error" : { "message" : "description of the error" }
}
Examples:
{
"error" : { "message" : "missing or invalid X-Proxy-Auth-Key" }
}
{
"error" : { "message" : "upstream fetch failed" }
}
Requirements Summary
Things Your Proxy MUST Do
Keep method, path, and query unchanged
Preserve body type: JSON, text, or binary
Pass all required headers (including Origin)
Add X-Plugin-Token header
Validate X-Proxy-Auth-Key before forwarding
Use correct error format
Return upstream response exactly (status, body, headers)
Forward binary responses (zip, pdf, xlsx) as-is
Things Your Proxy Must NOT Do
Do not rewrite paths
Do not remove fields from body
Do not add custom fields to body
Do not modify query parameters
Do not transform the response
Implementation Examples
Node.js/Express
Python/FastAPI
Python/Flask
Java/Spring Boot
Go
import express from "express" ;
import fetch from "node-fetch" ;
import bodyParser from "body-parser" ;
const app = express ();
app . use ( bodyParser . raw ({ type: "*/*" })); // preserve raw body
// --- Environment variables ---
const PROXY_AUTH_KEY = process . env . PROXY_AUTH_KEY ; // secret key for validating requests
const SELFHOSTED_BACKEND_URL = process . env . SELFHOSTED_BACKEND_URL ; // your Databrain backend
const GUEST_TOKEN = process . env . GUEST_TOKEN ; // plugin token (saved or generated via API)
app . all ( '/proxy-auth/*' , async ( req , res ) => {
try {
// --- Extract path and query ---
const upstreamPath = req . path . replace ( / ^ \/ proxy-auth/ , '' ) || '/' ;
const queryString = req . originalUrl . split ( '?' )[ 1 ] || '' ;
const upstreamUrl = ` ${ SELFHOSTED_BACKEND_URL }${ upstreamPath }${ queryString ? `? ${ queryString } ` : '' } ` ;
const method = req . method ;
const incomingHeaders = req . headers || {};
// --- Handle preflight OPTIONS request ---
if ( method === 'OPTIONS' ) {
return res . status ( 204 ). end ();
}
// --- Validate proxy key ---
const incomingProxyKey = incomingHeaders [ 'x-proxy-auth-key' ];
if (! incomingProxyKey || incomingProxyKey !== PROXY_AUTH_KEY ) {
return res . status ( 401 ). json ({
error: { message: 'missing or invalid X-Proxy-Auth-Key' },
});
}
// --- Build upstream headers ---
const upstreamHeaders : Record < string , any > = {
Accept: incomingHeaders [ 'accept' ] || '*/*' ,
'Accept-Language' : incomingHeaders [ 'accept-language' ] || 'en-US' ,
'User-Agent' : incomingHeaders [ 'user-agent' ] || 'Mozilla/5.0' ,
'X-Plugin-Token' : GUEST_TOKEN ,
};
if ( incomingHeaders [ 'content-type' ]) {
upstreamHeaders [ 'Content-Type' ] = incomingHeaders [ 'content-type' ];
}
if ( incomingHeaders [ 'x-plugin-origin' ]) {
upstreamHeaders [ 'X-Plugin-Origin' ] = incomingHeaders [ 'x-plugin-origin' ];
}
if ( incomingHeaders [ 'x-proxy-auth-url' ]) {
upstreamHeaders [ 'X-Proxy-Auth-Url' ] = incomingHeaders [ 'x-proxy-auth-url' ];
}
if ( incomingHeaders [ 'x-proxy-auth-key' ]) {
upstreamHeaders [ 'X-Proxy-Auth-Key' ] = incomingHeaders [ 'x-proxy-auth-key' ];
}
if ( incomingHeaders [ 'origin' ]) {
upstreamHeaders [ 'Origin' ] = incomingHeaders [ 'origin' ];
}
// --- Prepare request body for non-GET/HEAD methods ---
let upstreamBody ;
if (![ 'GET' , 'HEAD' ]. includes ( method )) {
upstreamBody = req . is ( 'application/json' ) ? JSON . stringify ( req . body ) : req . body ;
}
// --- Forward request to backend ---
let upstreamResponse ;
try {
upstreamResponse = await fetch ( upstreamUrl , {
method ,
headers: upstreamHeaders ,
body: upstreamBody ,
});
} catch ( err : any ) {
return res . status ( 502 ). json ({
error: { message: 'upstream fetch failed' },
detail: err . message ,
});
}
// --- Forward upstream headers as-is ---
const outHeaders : Record < string , any > = {};
upstreamResponse . headers . forEach (( v , k ) => ( outHeaders [ k ] = v ));
// --- Handle binary vs text responses ---
const contentType = upstreamResponse . headers . get ( 'content-type' ) || '' ;
if ( contentType . startsWith ( 'text/' ) || contentType . includes ( 'json' )) {
const text = await upstreamResponse . text ();
res . status ( upstreamResponse . status ). set ( outHeaders ). send ( text );
} else {
const buffer = Buffer . from ( await upstreamResponse . arrayBuffer ());
res . status ( upstreamResponse . status ). set ( outHeaders ). send ( buffer );
}
} catch ( err : any ) {
return res . status ( 500 ). json ({
error: { message: 'internal server error' },
detail: err . message ,
});
}
});
// --- Start server ---
app . listen ( 3000 , () => {
console . log ( 'Proxy server running on port 3000' );
});
from fastapi import FastAPI, Request, Response
from fastapi.responses import JSONResponse
import httpx
import os
app = FastAPI()
# --- Environment variables ---
PROXY_AUTH_KEY = os.getenv( "PROXY_AUTH_KEY" ) # secret key for validating requests
SELFHOSTED_BACKEND_URL = os.getenv( "SELFHOSTED_BACKEND_URL" ) # your Databrain backend
GUEST_TOKEN = os.getenv( "GUEST_TOKEN" ) # plugin token (saved or generated via API)
@app.api_route ( "/proxy-auth/ {path:path} " , methods =[ "GET" , "POST" , "PUT" , "PATCH" , "DELETE" , "OPTIONS" ])
async def proxy_handler ( request : Request, path : str ):
try :
method = request.method
# --- Handle preflight OPTIONS request ---
if method == "OPTIONS" :
return Response( status_code = 204 )
# --- Validate proxy key ---
incoming_proxy_key = request.headers.get( "x-proxy-auth-key" )
if not incoming_proxy_key or incoming_proxy_key != PROXY_AUTH_KEY:
return JSONResponse(
status_code = 401 ,
content ={ "error" : { "message" : "missing or invalid X-Proxy-Auth-Key" }}
)
# --- Construct upstream URL ---
query_string = str (request.query_params)
upstream_url = f " { SELFHOSTED_BACKEND_URL } / { path } "
if query_string:
upstream_url += f "? { query_string } "
# --- Build upstream headers ---
upstream_headers = {
"Accept" : request.headers.get( "accept" , "*/*" ),
"Accept-Language" : request.headers.get( "accept-language" , "en-US" ),
"User-Agent" : request.headers.get( "user-agent" , "Mozilla/5.0" ),
"X-Plugin-Token" : GUEST_TOKEN,
}
if content_type := request.headers.get( "content-type" ):
upstream_headers[ "Content-Type" ] = content_type
if plugin_origin := request.headers.get( "x-plugin-origin" ):
upstream_headers[ "X-Plugin-Origin" ] = plugin_origin
if proxy_auth_url := request.headers.get( "x-proxy-auth-url" ):
upstream_headers[ "X-Proxy-Auth-Url" ] = proxy_auth_url
if proxy_auth_key := request.headers.get( "x-proxy-auth-key" ):
upstream_headers[ "X-Proxy-Auth-Key" ] = proxy_auth_key
if origin := request.headers.get( "origin" ):
upstream_headers[ "Origin" ] = origin
# --- Get request body ---
body = await request.body() if method not in [ "GET" , "HEAD" ] else None
# --- Forward request to backend ---
async with httpx.AsyncClient() as client:
try :
upstream_response = await client.request(
method =method,
url =upstream_url,
headers =upstream_headers,
content =body,
)
except httpx.RequestError as e:
return JSONResponse(
status_code = 502 ,
content ={ "error" : { "message" : "upstream fetch failed" }, "detail" : str (e)}
)
# --- Forward upstream headers as-is ---
out_headers = dict (upstream_response.headers)
# Remove hop-by-hop headers
for header in [ "transfer-encoding" , "connection" , "keep-alive" ]:
out_headers.pop(header, None )
# --- Handle binary vs text responses ---
# Response.content is already bytes, works for both binary and text
return Response(
content =upstream_response.content,
status_code =upstream_response.status_code,
headers =out_headers,
media_type =upstream_response.headers.get( "content-type" ),
)
except Exception as e:
return JSONResponse(
status_code = 500 ,
content ={ "error" : { "message" : "internal server error" }, "detail" : str (e)}
)
if __name__ == "__main__" :
import uvicorn
uvicorn.run(app, host = "0.0.0.0" , port = 3000 )
from flask import Flask, request, Response, jsonify
import requests
import os
app = Flask( __name__ )
# --- Environment variables ---
PROXY_AUTH_KEY = os.getenv( "PROXY_AUTH_KEY" ) # secret key for validating requests
SELFHOSTED_BACKEND_URL = os.getenv( "SELFHOSTED_BACKEND_URL" ) # your Databrain backend
GUEST_TOKEN = os.getenv( "GUEST_TOKEN" ) # plugin token (saved or generated via API)
@app.route ( "/proxy-auth/<path:path>" , methods =[ "GET" , "POST" , "PUT" , "PATCH" , "DELETE" , "OPTIONS" ])
def proxy_handler ( path ):
try :
method = request.method
# --- Handle preflight OPTIONS request ---
if method == "OPTIONS" :
return Response( status = 204 )
# --- Validate proxy key ---
incoming_proxy_key = request.headers.get( "X-Proxy-Auth-Key" )
if not incoming_proxy_key or incoming_proxy_key != PROXY_AUTH_KEY:
return jsonify({ "error" : { "message" : "missing or invalid X-Proxy-Auth-Key" }}), 401
# --- Construct upstream URL ---
query_string = request.query_string.decode( "utf-8" )
upstream_url = f " { SELFHOSTED_BACKEND_URL } / { path } "
if query_string:
upstream_url += f "? { query_string } "
# --- Build upstream headers ---
upstream_headers = {
"Accept" : request.headers.get( "Accept" , "*/*" ),
"Accept-Language" : request.headers.get( "Accept-Language" , "en-US" ),
"User-Agent" : request.headers.get( "User-Agent" , "Mozilla/5.0" ),
"X-Plugin-Token" : GUEST_TOKEN,
}
if content_type := request.headers.get( "Content-Type" ):
upstream_headers[ "Content-Type" ] = content_type
if plugin_origin := request.headers.get( "X-Plugin-Origin" ):
upstream_headers[ "X-Plugin-Origin" ] = plugin_origin
if proxy_auth_url := request.headers.get( "X-Proxy-Auth-Url" ):
upstream_headers[ "X-Proxy-Auth-Url" ] = proxy_auth_url
if proxy_auth_key := request.headers.get( "X-Proxy-Auth-Key" ):
upstream_headers[ "X-Proxy-Auth-Key" ] = proxy_auth_key
if origin := request.headers.get( "Origin" ):
upstream_headers[ "Origin" ] = origin
# --- Get request body ---
body = request.get_data() if method not in [ "GET" , "HEAD" ] else None
# --- Forward request to backend ---
try :
upstream_response = requests.request(
method =method,
url =upstream_url,
headers =upstream_headers,
data =body,
)
except requests.RequestException as e:
return jsonify({ "error" : { "message" : "upstream fetch failed" }, "detail" : str (e)}), 502
# --- Forward upstream headers as-is ---
out_headers = dict (upstream_response.headers)
# Remove hop-by-hop headers
for header in [ "Transfer-Encoding" , "Connection" , "Keep-Alive" ]:
out_headers.pop(header, None )
# --- Handle binary vs text responses ---
# upstream_response.content is already bytes, works for both binary and text
return Response(
response =upstream_response.content,
status =upstream_response.status_code,
headers =out_headers,
mimetype =upstream_response.headers.get( "Content-Type" ),
)
except Exception as e:
return jsonify({ "error" : { "message" : "internal server error" }, "detail" : str (e)}), 500
if __name__ == "__main__" :
app.run( host = "0.0.0.0" , port = 3000 )
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.RestClientException;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
import java.util.stream.Collectors;
@ RestController
@ RequestMapping ( "/proxy-auth" )
public class DatabrainProxyController {
@ Value ( "${PROXY_AUTH_KEY}" )
private String proxyAuthKey ;
@ Value ( "${SELFHOSTED_BACKEND_URL}" )
private String selfhostedBackendUrl ;
@ Value ( "${GUEST_TOKEN}" )
private String guestToken ;
private final RestTemplate restTemplate = new RestTemplate ();
@ RequestMapping (value = "/**" , method = {
RequestMethod . GET , RequestMethod . POST , RequestMethod . PUT ,
RequestMethod . PATCH , RequestMethod . DELETE , RequestMethod . OPTIONS
})
public ResponseEntity < ? > proxyHandler (
HttpServletRequest request ,
@ RequestBody (required = false ) byte [] body ,
@ RequestHeader Map < String , String > headers
) {
try {
String method = request . getMethod ();
// --- Handle preflight OPTIONS request ---
if ( "OPTIONS" . equals (method)) {
return ResponseEntity . status ( 204 ). build ();
}
// --- Validate proxy key ---
String incomingProxyKey = headers . get ( "x-proxy-auth-key" );
if (incomingProxyKey == null || ! incomingProxyKey . equals (proxyAuthKey)) {
return ResponseEntity . status ( 401 ). body ( Map . of (
"error" , Map . of ( "message" , "missing or invalid X-Proxy-Auth-Key" )
));
}
// --- Construct upstream URL ---
String path = request . getRequestURI (). replaceFirst ( "^/proxy-auth" , "" );
String queryString = request . getQueryString ();
String upstreamUrl = selfhostedBackendUrl + path;
if (queryString != null && ! queryString . isEmpty ()) {
upstreamUrl += "?" + queryString;
}
// --- Build upstream headers ---
HttpHeaders upstreamHeaders = new HttpHeaders ();
upstreamHeaders . set ( "Accept" , headers . getOrDefault ( "accept" , "*/*" ));
upstreamHeaders . set ( "Accept-Language" , headers . getOrDefault ( "accept-language" , "en-US" ));
upstreamHeaders . set ( "User-Agent" , headers . getOrDefault ( "user-agent" , "Mozilla/5.0" ));
upstreamHeaders . set ( "X-Plugin-Token" , guestToken);
if ( headers . containsKey ( "content-type" )) {
upstreamHeaders . set ( "Content-Type" , headers . get ( "content-type" ));
}
if ( headers . containsKey ( "x-plugin-origin" )) {
upstreamHeaders . set ( "X-Plugin-Origin" , headers . get ( "x-plugin-origin" ));
}
if ( headers . containsKey ( "x-proxy-auth-url" )) {
upstreamHeaders . set ( "X-Proxy-Auth-Url" , headers . get ( "x-proxy-auth-url" ));
}
if ( headers . containsKey ( "x-proxy-auth-key" )) {
upstreamHeaders . set ( "X-Proxy-Auth-Key" , headers . get ( "x-proxy-auth-key" ));
}
if ( headers . containsKey ( "origin" )) {
upstreamHeaders . set ( "Origin" , headers . get ( "origin" ));
}
// --- Prepare request entity ---
HttpEntity < byte []> requestEntity = new HttpEntity <>(
"GET" . equals (method) || "HEAD" . equals (method) ? null : body,
upstreamHeaders
);
// --- Forward request to backend ---
ResponseEntity < byte []> upstreamResponse ;
try {
upstreamResponse = restTemplate . exchange (
upstreamUrl,
HttpMethod . valueOf (method),
requestEntity,
byte []. class
);
} catch ( RestClientException e ) {
return ResponseEntity . status ( 502 ). body ( Map . of (
"error" , Map . of ( "message" , "upstream fetch failed" ),
"detail" , e . getMessage ()
));
}
// --- Forward upstream headers as-is ---
HttpHeaders outHeaders = new HttpHeaders ();
upstreamResponse . getHeaders (). forEach ((key, values) -> {
// Skip hop-by-hop headers
if (! key . equalsIgnoreCase ( "Transfer-Encoding" ) &&
! key . equalsIgnoreCase ( "Connection" ) &&
! key . equalsIgnoreCase ( "Keep-Alive" )) {
outHeaders . put (key, values);
}
});
// --- Handle binary vs text responses ---
// byte[] response body works for both binary and text content
return ResponseEntity
. status ( upstreamResponse . getStatusCode ())
. headers (outHeaders)
. body ( upstreamResponse . getBody ());
} catch ( Exception e ) {
return ResponseEntity . status ( 500 ). body ( Map . of (
"error" , Map . of ( "message" , "internal server error" ),
"detail" , e . getMessage ()
));
}
}
}
package main
import (
"io"
"log"
"net/http"
"os"
"strings"
)
var (
proxyAuthKey = os . Getenv ( "PROXY_AUTH_KEY" )
selfhostedBackendURL = os . Getenv ( "SELFHOSTED_BACKEND_URL" )
guestToken = os . Getenv ( "GUEST_TOKEN" )
)
func proxyHandler ( w http . ResponseWriter , r * http . Request ) {
// --- Handle preflight OPTIONS request ---
if r . Method == "OPTIONS" {
w . WriteHeader ( http . StatusNoContent )
return
}
// --- Validate proxy key ---
incomingProxyKey := r . Header . Get ( "X-Proxy-Auth-Key" )
if incomingProxyKey == "" || incomingProxyKey != proxyAuthKey {
w . Header (). Set ( "Content-Type" , "application/json" )
w . WriteHeader ( http . StatusUnauthorized )
w . Write ([] byte ( `{"error":{"message":"missing or invalid X-Proxy-Auth-Key"}}` ))
return
}
// --- Construct upstream URL ---
path := strings . TrimPrefix ( r . URL . Path , "/proxy-auth" )
if path == "" {
path = "/"
}
upstreamURL := selfhostedBackendURL + path
if r . URL . RawQuery != "" {
upstreamURL += "?" + r . URL . RawQuery
}
// --- Read request body ---
var body io . Reader
if r . Method != "GET" && r . Method != "HEAD" {
body = r . Body
defer r . Body . Close ()
}
// --- Create upstream request ---
upstreamReq , err := http . NewRequest ( r . Method , upstreamURL , body )
if err != nil {
w . Header (). Set ( "Content-Type" , "application/json" )
w . WriteHeader ( http . StatusInternalServerError )
w . Write ([] byte ( `{"error":{"message":"internal server error"}}` ))
return
}
// --- Build upstream headers ---
upstreamReq . Header . Set ( "Accept" , getOrDefault ( r . Header . Get ( "Accept" ), "*/*" ))
upstreamReq . Header . Set ( "Accept-Language" , getOrDefault ( r . Header . Get ( "Accept-Language" ), "en-US" ))
upstreamReq . Header . Set ( "User-Agent" , getOrDefault ( r . Header . Get ( "User-Agent" ), "Mozilla/5.0" ))
upstreamReq . Header . Set ( "X-Plugin-Token" , guestToken )
if ct := r . Header . Get ( "Content-Type" ); ct != "" {
upstreamReq . Header . Set ( "Content-Type" , ct )
}
if po := r . Header . Get ( "X-Plugin-Origin" ); po != "" {
upstreamReq . Header . Set ( "X-Plugin-Origin" , po )
}
if pau := r . Header . Get ( "X-Proxy-Auth-Url" ); pau != "" {
upstreamReq . Header . Set ( "X-Proxy-Auth-Url" , pau )
}
if pak := r . Header . Get ( "X-Proxy-Auth-Key" ); pak != "" {
upstreamReq . Header . Set ( "X-Proxy-Auth-Key" , pak )
}
if origin := r . Header . Get ( "Origin" ); origin != "" {
upstreamReq . Header . Set ( "Origin" , origin )
}
// --- Forward request to backend ---
client := & http . Client {}
upstreamResp , err := client . Do ( upstreamReq )
if err != nil {
w . Header (). Set ( "Content-Type" , "application/json" )
w . WriteHeader ( http . StatusBadGateway )
w . Write ([] byte ( `{"error":{"message":"upstream fetch failed"},"detail":"` + err . Error () + `"}` ))
return
}
defer upstreamResp . Body . Close ()
// --- Forward upstream headers as-is ---
for key , values := range upstreamResp . Header {
// Skip hop-by-hop headers
lowerKey := strings . ToLower ( key )
if lowerKey == "transfer-encoding" || lowerKey == "connection" || lowerKey == "keep-alive" {
continue
}
for _ , value := range values {
w . Header (). Add ( key , value )
}
}
// --- Forward upstream response (handles both binary and text) ---
w . WriteHeader ( upstreamResp . StatusCode )
io . Copy ( w , upstreamResp . Body )
}
func getOrDefault ( value , defaultValue string ) string {
if value == "" {
return defaultValue
}
return value
}
func main () {
http . HandleFunc ( "/proxy-auth/" , proxyHandler )
log . Println ( "Proxy server running on port 3000" )
log . Fatal ( http . ListenAndServe ( ":3000" , nil ))
}
Frontend Configuration
Configure your frontend to send requests through your proxy server instead of directly to the Databrain backend.
Configuration Setup
React
Vue.js
Vanilla JavaScript
import { Databrain } from '@databrainhq/plugin' ;
// Configure proxy authentication globally
if ( typeof window !== 'undefined' ) {
window . dbn = {
proxyAuthUrl: 'https://your-proxy-server.com/proxy-auth' ,
proxyAuthKey: 'your-proxy-authentication-key' ,
isEnableProxyAuth: true
};
}
function MyDashboard () {
return (
< Databrain
dashboardId = "your-dashboard-id"
// No token prop needed - proxy handles authentication
/>
);
}
TypeScript Users: You need to declare the global window.dbn type in your project:declare global {
interface Window {
dbn ?: {
proxyAuthUrl ?: string ;
proxyAuthKey ?: string ;
isEnableProxyAuth ?: boolean ;
};
}
}
Configuration Properties
The full URL of your proxy endpoint. This is sent as the X-Proxy-Auth-Url header. Example: https://your-proxy-server.com/proxy-auth
A secret authentication key sent as the X-Proxy-Auth-Key header for your proxy to validate. Generate a strong, random key and store it securely. This key must match the one your proxy expects.
window.dbn.isEnableProxyAuth
Enable or disable proxy authentication mode.
true: Use proxy authentication (required for proxy auth to work)
false or omitted: Use direct token authentication
Environment Variables
Your proxy server requires these environment variables:
Variable Description PROXY_AUTH_KEYSecret key to validate incoming requests (must match frontend proxyAuthKey) SELFHOSTED_BACKEND_URLYour Databrain backend URL (e.g., https://api.usedatabrain.com) GUEST_TOKENYour plugin token (saved or generated via API )
Security Best Practices
Use Strong Keys: Generate a cryptographically random proxy authentication key
Validate Every Request: Always verify the X-Proxy-Auth-Key header
HTTPS Only: Ensure your proxy endpoint is only accessible via HTTPS
Rotate Keys: Periodically rotate your proxy authentication keys
Add rate limiting to prevent abuse:
Limit requests per IP address
Limit requests per user/session
Implement exponential backoff for repeated failures
Log all proxy requests for audit purposes
Monitor for unusual traffic patterns
Set up alerts for authentication failures
Store GUEST_TOKEN securely in environment variables
Never expose tokens in client-side code
Consider generating tokens dynamically for enhanced security
Error Handling
Common Errors
401 - Missing or Invalid Proxy Key
Error: {"error": {"message": "missing or invalid X-Proxy-Auth-Key"}}Cause: The X-Proxy-Auth-Key header is missing or doesn’t match.Solution: Verify that window.dbn.proxyAuthKey matches your proxy’s PROXY_AUTH_KEY environment variable.
502 - Upstream Fetch Failed
Error: {"error": {"message": "upstream fetch failed"}}Cause: Your proxy cannot reach the Databrain backend.Solution:
Verify SELFHOSTED_BACKEND_URL is correct
Check network connectivity from your proxy server
Verify firewall rules allow outbound connections
500 - Internal Server Error
Error: {"error": {"message": "internal server error"}}Cause: An unexpected error occurred in your proxy.Solution: Check your proxy server logs for detailed error information.
Testing Your Proxy
Use curl to test your proxy endpoint:
# Test with valid proxy key
curl -X GET "https://your-proxy-server.com/proxy-auth/api/health" \
-H "X-Proxy-Auth-Key: your-proxy-key"
# Test authentication validation (should return 401)
curl -X GET "https://your-proxy-server.com/proxy-auth/api/health" \
-H "X-Proxy-Auth-Key: invalid-key"