Skip to content

Commit 0089f76

Browse files
authored
Merge pull request #1 from Altinity/add_cors_to_sse
Add cors to sse
2 parents d8e5fde + d3e1694 commit 0089f76

File tree

9 files changed

+59
-44
lines changed

9 files changed

+59
-44
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,4 @@ $RECYCLE.BIN/
365365

366366
# Local configs
367367
*.secret.yaml
368+
.aider*

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<div align="center">
22

3-
![Build Binaries](https://github.com/centralmind/gateway/actions/workflows/build-binaries.yml/badge.svg) &nbsp; <a href="https://discord.gg/XFhaUG4F5x"><img src="https://dcbadge.limes.pink/api/server/https://discord.gg/XFhaUG4F5x" height="20"></a> &nbsp;&nbsp;<a href="https://t.me/+TM3T1SikjzA4ZWVi"><img src="https://img.shields.io/badge/telegram-%E2%9D%A4%EF%B8%8F-252850?style=plastic&logo=telegram" height=20></a> &nbsp;&nbsp; <a href="https://docs.centralmind.ai"><img src="https://img.shields.io/badge/Full%20Documentation-blue?style=for-the-badge&logo=rocket&logoColor=white" height="20"></a>
3+
![Build Binaries](https://github.com/centralmind/gateway/actions/workflows/build-binaries.yml/badge.svg) &nbsp; <a href="https://discord.gg/XFhaUG4F5x"><img src="https://dcbadge.limes.pink/api/server/https://discord.gg/XFhaUG4F5x" height="20"></a> &nbsp;&nbsp;<a href="https://t.me/+TM3T1SikjzA4ZWVi"><img src="https://img.shields.io/badge/telegram-%E2%9D%A4%EF%B8%8F-252850?style=plastic&logo=telegram" height=20></a> &nbsp;&nbsp; <a href="https://docs.centralmind.ai"><img src="https://img.shields.io/badge/Full%20Documentation-blue?style=for-the-badge&logo=rocket&logoColor=white" height="20"></a>&nbsp;&nbsp; <a href="https://cursor.com/install-mcp?name=CentralMind%20Database%20Gateway&config=eyJjb21tYW5kIjoiZG9ja2VyIHJ1biAtaSAtLXBsYXRmb3JtIGxpbnV4L2FtZDY0IGdoY3IuaW8vY2VudHJhbG1pbmQvZ2F0ZXdheTp2MC4yLjE4IC0tY29ubmVjdGlvbi1zdHJpbmcgcG9zdGdyZXNxbDovL215X3VzZXI6bXlfcGFzc0Bsb2NhbGhvc3Q6NTQzMi9teWRiIHN0YXJ0IHN0ZGlvIn0%3D"><img height="21" src="https://cursor.com/deeplink/mcp-install-dark.svg"></a>
4+
45

56
</div>
67

@@ -15,7 +16,7 @@ Simple way to expose your database to AI-Agent via MCP or OpenAPI 3.1 protocols.
1516

1617
```bash
1718
docker run --platform linux/amd64 -p 9090:9090 \
18-
ghcr.io/centralmind/gateway:v0.2.14 start \
19+
ghcr.io/centralmind/gateway:v0.2.18 start \
1920
--connection-string "postgres://db-user:db-password@db-host/db-name?sslmode=require"
2021
```
2122

cors/cors.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package cors
2+
3+
import "net/http"
4+
5+
// ApplyCORSHeaders adds the standard CORS headers to a response
6+
// For handlers that are not wrapped in middleware
7+
func ApplyCORSHeaders(w http.ResponseWriter, allowedMethods string) {
8+
w.Header().Set("Access-Control-Allow-Origin", "*")
9+
w.Header().Set("Access-Control-Allow-Methods", allowedMethods+", OPTIONS")
10+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, mcp-protocol-version")
11+
}
12+
13+
// HandlePreflight checks if the request is a preflight OPTIONS request and handles it
14+
// Returns true if the request was handled (caller should return immediately)
15+
func HandlePreflight(w http.ResponseWriter, r *http.Request) bool {
16+
if r.Method == http.MethodOptions {
17+
w.WriteHeader(http.StatusOK)
18+
return true
19+
}
20+
return false
21+
}

plugins/oauth/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ oauth:
202202
client_id: "xxx"
203203
client_secret: "xxx"
204204
user_info_url: "https://your-tenant.auth0.com/userinfo"
205+
provider_auth_url: "https://your-tenant.auth0.com/authorize"
206+
provider_token_url: "https://your-tenant.auth0.com/oauth/token"
205207
scopes:
206208
- "openid"
207209
- "profile"

plugins/oauth/auth_metadata.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,26 @@ type Metadata struct {
1515
}
1616

1717
func NewMetadata(issuer url.URL, authorizationEndpoint, tokenEndpoint string, registrationEndpoint string) Metadata {
18+
buildURL := func(endpoint string) string {
19+
if u, err := url.Parse(endpoint); err == nil && u.IsAbs() {
20+
return endpoint
21+
}
22+
return (&url.URL{Scheme: issuer.Scheme, Host: issuer.Host, Path: endpoint}).String()
23+
}
24+
1825
metadata := Metadata{
1926
Issuer: issuer.String(),
20-
AuthorizationEndpoint: (&url.URL{Scheme: issuer.Scheme, Host: issuer.Host, Path: authorizationEndpoint}).String(),
27+
AuthorizationEndpoint: buildURL(authorizationEndpoint),
2128
ResponseTypesSupported: []string{"code"},
2229
CodeChallengeMethodsSupported: []string{"S256"},
23-
TokenEndpoint: (&url.URL{Scheme: issuer.Scheme, Host: issuer.Host, Path: tokenEndpoint}).String(),
30+
TokenEndpoint: buildURL(tokenEndpoint),
2431
TokenEndpointAuthMethodsSupported: []string{"client_secret_post"},
2532
GrantTypesSupported: []string{"authorization_code", "refresh_token"},
2633
}
2734

2835
// Add registration endpoint if provided
2936
if registrationEndpoint != "" {
30-
metadata.RegistrationEndpoint = (&url.URL{Scheme: issuer.Scheme, Host: issuer.Host, Path: registrationEndpoint}).String()
37+
metadata.RegistrationEndpoint = buildURL(registrationEndpoint)
3138
}
3239

3340
return metadata

plugins/oauth/config.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,16 +147,16 @@ func (c *Config) WithDefaults() {
147147
c.TokenHeader = "Authorization"
148148
}
149149
if c.AuthURL == "" {
150-
c.AuthURL = "/oauth/authorize"
150+
c.AuthURL = "/oauth/authorize/"
151151
}
152152
if c.CallbackURL == "" {
153-
c.CallbackURL = "/oauth/callback"
153+
c.CallbackURL = "/oauth/callback/"
154154
}
155155
if c.TokenURL == "" {
156-
c.TokenURL = "/oauth/token"
156+
c.TokenURL = "/oauth/token/"
157157
}
158158
if c.RegisterURL == "" {
159-
c.RegisterURL = "/oauth/register"
159+
c.RegisterURL = "/oauth/register/"
160160
}
161161
if c.ClientRegistration.ClientSecretExpirySeconds == 0 {
162162
c.ClientRegistration.ClientSecretExpirySeconds = 30 * 24 * 60 * 60 // 30 days

plugins/oauth/http_helpers.go

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,18 @@
11
package oauth
22

33
import (
4+
"github.com/centralmind/gateway/cors"
45
"net/http"
56
)
67

78
// CORSMiddleware applies standard CORS headers to the response
89
func CORSMiddleware(handler http.Handler) http.Handler {
910
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
10-
// Add CORS headers
11-
w.Header().Set("Access-Control-Allow-Origin", "*")
12-
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
13-
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
14-
15-
// Handle preflight OPTIONS request
16-
if r.Method == "OPTIONS" {
17-
w.WriteHeader(http.StatusOK)
11+
cors.ApplyCORSHeaders(w, "GET, POST")
12+
if cors.HandlePreflight(w, r) {
1813
return
1914
}
20-
2115
// Call the original handler
2216
handler.ServeHTTP(w, r)
2317
})
2418
}
25-
26-
// ApplyCORSHeaders adds the standard CORS headers to a response
27-
// For handlers that are not wrapped in middleware
28-
func ApplyCORSHeaders(w http.ResponseWriter, allowedMethods string) {
29-
w.Header().Set("Access-Control-Allow-Origin", "*")
30-
w.Header().Set("Access-Control-Allow-Methods", allowedMethods+", OPTIONS")
31-
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
32-
}
33-
34-
// HandlePreflight checks if the request is a preflight OPTIONS request and handles it
35-
// Returns true if the request was handled (caller should return immediately)
36-
func HandlePreflight(w http.ResponseWriter, r *http.Request) bool {
37-
if r.Method == "OPTIONS" {
38-
w.WriteHeader(http.StatusOK)
39-
return true
40-
}
41-
return false
42-
}

plugins/oauth/plugin.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,17 @@ func (p *Plugin) RegisterRoutes(mux *http.ServeMux) {
148148
if err != nil {
149149
return
150150
}
151+
152+
getPath := func(rawURL string) string {
153+
if u, urlParseErr := url.Parse(rawURL); urlParseErr == nil && u.Path != "" {
154+
return u.Path
155+
}
156+
return rawURL
157+
}
158+
151159
// Register HTTP handlers with CORS middleware
152-
mux.Handle(p.config.AuthURL, CORSMiddleware(http.HandlerFunc(p.HandleAuthorize)))
153-
mux.Handle(p.config.CallbackURL, CORSMiddleware(http.HandlerFunc(p.HandleCallback)))
160+
mux.Handle(getPath(p.config.AuthURL), CORSMiddleware(http.HandlerFunc(p.HandleAuthorize)))
161+
mux.Handle(getPath(p.config.CallbackURL), CORSMiddleware(http.HandlerFunc(p.HandleCallback)))
154162

155163
// Set up and register the token endpoint
156164
tokenPath := p.config.TokenURL // Use the configured token URL
@@ -160,7 +168,7 @@ func (p *Plugin) RegisterRoutes(mux *http.ServeMux) {
160168

161169
// Register the token handler with CORS middleware
162170
tokenHandler := http.HandlerFunc(p.HandleToken)
163-
mux.Handle(tokenPath, CORSMiddleware(tokenHandler))
171+
mux.Handle(getPath(tokenPath), CORSMiddleware(tokenHandler))
164172

165173
// Register dynamic client registration endpoint if enabled
166174
if p.config.ClientRegistration.Enabled {
@@ -169,7 +177,7 @@ func (p *Plugin) RegisterRoutes(mux *http.ServeMux) {
169177

170178
// Register the handler with CORS middleware
171179
registrationHandler := http.HandlerFunc(p.HandleRegister)
172-
mux.Handle(p.config.RegisterURL, CORSMiddleware(registrationHandler))
180+
mux.Handle(getPath(p.config.RegisterURL), CORSMiddleware(registrationHandler))
173181
}
174182

175183
// Register the well-known endpoint with CORS middleware

server/sse.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"github.com/centralmind/gateway/cors"
78
"github.com/centralmind/gateway/xcontext"
89
"net/http"
910
"net/http/httptest"
@@ -87,10 +88,8 @@ func (s *SSEServer) handleSSE(w http.ResponseWriter, r *http.Request) {
8788
w.Header().Set("Content-Type", "text/event-stream")
8889
w.Header().Set("Cache-Control", "no-cache")
8990
w.Header().Set("Connection", "keep-alive")
90-
w.Header().Set("Access-Control-Allow-Origin", "*")
91-
92-
if r.Method == http.MethodOptions {
93-
w.WriteHeader(http.StatusOK)
91+
cors.ApplyCORSHeaders(w, "GET")
92+
if cors.HandlePreflight(w, r) {
9493
return
9594
}
9695

0 commit comments

Comments
 (0)