Skip to main content

🔐 Secure your LLM provider endpoint

Video Turorial

Introduction

To secure your LLM provider endpoint http://openai-demo.oto.tools:8080 for example, you could add API keys.

To do it so, add the API Keys plugin to your route :

Once added you should see your route as :

Then, we will try to send a request to our endpoint http://openai-demo.oto.tools:8080

curl http://openai-demo.oto.tools:8080 \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello how are you ?"
}
]
}'

The endpoint will return an error :

{
"Otoroshi-Error": "no apikey"
}

That's good ! Because we just use our endpoint without any API Key and our request got rejected.

Create new API Key

Go to API Keys dashboard and create a new Key

Go to Authorized On section and select your route. OpenAI Demo in our example case.

Copy the Bearer token that may look like :

otoapk_urjyypqbif8gr8wx_701cd2e99c0e2707e44bf6a7bb75518c90700a867d6c22f624020b22434cf7f9

And then re-send a new request with our Bearer token into our request headers.

 curl http://openai-demo.oto.tools:8080 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer otoapk_urjyypqbif8gr8wx_701cd2e99c0e2707e44bf6a7bb75518c90700a867d6c22f624020b22434cf7f9" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello how are you ?"
}
]
}'

Ta-da !

Here is our real response from the LLM with our authorized API key :

{
"id": "chatcmpl-SySPnMI3VzZKQZbvT42ko0H5YhrcIAVB",
"object": "chat.completion",
"created": 1743417992,
"model": "gpt-4o-mini",
"system_fingerprint": "fp-XfvVJ9gJ2viprCxZ1pyq508tukNbuz8u",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! I'm just a computer program, so I don't have feelings, but I'm here and ready to help you. How can I assist you today?"
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 31,
"total_tokens": 43,
"completion_tokens_details": {
"reasoning_tokens": 0
}
}
}

We can see in our API Key that the quotas for our API keys has been decreased due our request

Full configuration

Configuration for the API Key

{
"_loc": {
"tenant": "default",
"teams": [
"default"
]
},
"clientId": "urjyypqbif8gr8wx",
"clientSecret": "5icbe4o94dbwere7nupoecfmgr5kbo3cgpp0iv94gf1kxak04ocfw9tvu4f9nrmd",
"clientName": "Hilario Flatley's api-key",
"description": "",
"authorizedGroup": null,
"authorizedEntities": [
"route_route_ca396e0a1-2bc6-41c5-a682-2a95c4ed0c24"
],
"authorizations": [
{
"kind": "route",
"id": "route_ca396e0a1-2bc6-41c5-a682-2a95c4ed0c24"
}
],
"enabled": true,
"readOnly": false,
"allowClientIdOnly": false,
"throttlingQuota": 10000000,
"dailyQuota": 10000000,
"monthlyQuota": 10000000,
"constrainedServicesOnly": false,
"restrictions": {
"enabled": false,
"allowLast": true,
"allowed": [],
"forbidden": [],
"notFound": []
},
"rotation": {
"enabled": false,
"rotationEvery": 744,
"gracePeriod": 168,
"nextSecret": null
},
"validUntil": null,
"tags": [],
"metadata": {
"created_at": "2025-03-31T12:42:40.194+02:00"
},
"bearer": "otoapk_urjyypqbif8gr8wx_701cd2e99c0e2707e44bf6a7bb75518c90700a867d6c22f624020b22434cf7f9",
"kind": "apim.otoroshi.io/ApiKey"
}

Full route configuration with API Keys plugin :

{
"_loc": {
"tenant": "default",
"teams": [
"default"
]
},
"id": "route_ca396e0a1-2bc6-41c5-a682-2a95c4ed0c24",
"name": "OpenAI Demo",
"description": "OpenAI Demo",
"tags": [],
"metadata": {},
"enabled": true,
"debug_flow": false,
"export_reporting": false,
"capture": false,
"groups": [
"default"
],
"bound_listeners": [],
"frontend": {
"domains": [
"openai-demo.oto.tools"
],
"strip_path": true,
"exact": false,
"headers": {},
"query": {},
"methods": []
},
"backend": {
"targets": [
{
"id": "target_1",
"hostname": "request.otoroshi.io",
"port": 443,
"tls": true,
"weight": 1,
"predicate": {
"type": "AlwaysMatch"
},
"protocol": "HTTP/1.1",
"ip_address": null,
"tls_config": {
"certs": [],
"trusted_certs": [],
"enabled": false,
"loose": false,
"trust_all": false
}
}
],
"root": "/",
"rewrite": false,
"load_balancing": {
"type": "RoundRobin"
},
"client": {
"retries": 1,
"max_errors": 20,
"retry_initial_delay": 50,
"backoff_factor": 2,
"call_timeout": 30000,
"call_and_stream_timeout": 120000,
"connection_timeout": 10000,
"idle_timeout": 60000,
"global_timeout": 30000,
"sample_interval": 2000,
"proxy": {},
"custom_timeouts": [],
"cache_connection_settings": {
"enabled": false,
"queue_size": 2048
}
},
"health_check": {
"enabled": false,
"url": "",
"timeout": 5000,
"healthyStatuses": [],
"unhealthyStatuses": []
}
},
"backend_ref": null,
"plugins": [
{
"enabled": true,
"debug": false,
"plugin": "cp:otoroshi.next.plugins.OverrideHost",
"include": [],
"exclude": [],
"config": {},
"bound_listeners": [],
"plugin_index": {
"transform_request": 0
}
},
{
"enabled": true,
"debug": false,
"plugin": "cp:otoroshi_plugins.com.cloud.apim.otoroshi.extensions.aigateway.plugins.OpenAiCompatProxy",
"include": [],
"exclude": [],
"config": {
"refs": [
"provider_7e618efb-9c2c-42ed-870a-58b1ec3c6264"
]
},
"bound_listeners": [],
"plugin_index": {}
},
{
"enabled": true,
"debug": false,
"plugin": "cp:otoroshi.next.plugins.ApikeyCalls",
"include": [],
"exclude": [],
"config": {
"extractors": {
"basic": {
"enabled": true,
"header_name": null,
"query_name": null
},
"custom_headers": {
"enabled": true,
"client_id_header_name": null,
"client_secret_header_name": null
},
"client_id": {
"enabled": true,
"header_name": null,
"query_name": null
},
"jwt": {
"enabled": true,
"secret_signed": true,
"keypair_signed": true,
"include_request_attrs": false,
"max_jwt_lifespan_sec": null,
"header_name": null,
"query_name": null,
"cookie_name": null
}
},
"routing": {
"enabled": false
},
"validate": true,
"mandatory": true,
"pass_with_user": false,
"wipe_backend_request": true,
"update_quotas": true
},
"bound_listeners": [],
"plugin_index": {
"validate_access": 0,
"transform_request": 1,
"match_route": 0
}
}
],
"kind": "proxy.otoroshi.io/Route"
}