Skip to content
Merged
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
52 changes: 48 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ If this project saves you time, please give it a star — it really helps visibi
docker run --rm -p 80:80 --net=host \
-e EXTERNAL_URL=http://localhost \
-e PROXY_URL=http://localhost:8080 \
-e GLOBAL_SECRET=$(openssl rand -hex 32) \
-e PASSWORD=changeme \
-v ./data:/data \
ghcr.io/sigbit/mcp-auth-proxy:latest
Expand Down Expand Up @@ -53,7 +52,6 @@ For a simpler approach to publish local MCP servers over OAuth, consider [MCP Wa
| `DATA_PATH` | No | Data directory path | `./data` |
| `EXTERNAL_URL` | No | External URL for OAuth callbacks | `http://localhost` |
| `PROXY_URL` | No | Target MCP server URL | `http://localhost:8080` |
| `GLOBAL_SECRET` | No | Global secret for session encryption | `supersecret` |
| `GOOGLE_CLIENT_ID` | No | Google OAuth client ID | - |
| `GOOGLE_CLIENT_SECRET` | No | Google OAuth client secret | - |
| `GOOGLE_ALLOWED_USERS` | No | Comma-separated list of allowed Google emails | - |
Expand Down Expand Up @@ -87,7 +85,6 @@ Download the latest binary from [releases](https://github.com/sigbit/mcp-auth-pr
./mcp-auth-proxy \
--external-url "http://localhost:8081" \
--proxy-url "http://localhost:8080" \
--global-secret "$(openssl rand -hex 32)" \
--google-client-id "your-google-client-id" \
--google-client-secret "your-google-client-secret" \
--google-allowed-users "[email protected],[email protected]" \
Expand All @@ -103,7 +100,6 @@ Download the latest binary from [releases](https://github.com/sigbit/mcp-auth-pr
docker run --rm -p 8081:8081 --net=host \
-e EXTERNAL_URL=http://localhost:8081 \
-e PROXY_URL=http://localhost:8080 \
-e GLOBAL_SECRET=$(openssl rand -hex 32) \
-e GOOGLE_CLIENT_ID="your-google-client-id" \
-e GOOGLE_CLIENT_SECRET="your-google-client-secret" \
-e GOOGLE_ALLOWED_USERS="[email protected],[email protected]" \
Expand All @@ -114,3 +110,51 @@ docker run --rm -p 8081:8081 --net=host \
-v ./data:/data \
ghcr.io/sigbit/mcp-auth-proxy:latest
```

## 👨‍💻 For Developers

### Commit Message Guidelines

This project follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification for commit messages. This helps with automated versioning, changelog generation, and makes the commit history more readable.

#### Format

```
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
```

#### Types

- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation only changes
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- **refactor**: A code change that neither fixes a bug nor adds a feature
- **perf**: A code change that improves performance
- **test**: Adding missing tests or correcting existing tests
- **build**: Changes that affect the build system or external dependencies
- **ci**: Changes to our CI configuration files and scripts
- **chore**: Other changes that don't modify src or test files
- **revert**: Reverts a previous commit

#### Examples

```
feat: add GitHub OAuth provider support
fix: resolve token expiration handling
docs: update OAuth setup instructions
refactor: simplify authentication middleware
ci: add automated release workflow
```

#### Breaking Changes

Breaking changes should be indicated by a `!` after the type/scope:

```
feat!: change authentication API to support multiple providers
```
3 changes: 0 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ func main() {
var dataPath string
var externalURL string
var proxyURL string
var globalSecret string
var googleClientID string
var googleClientSecret string
var googleAllowedUsers string
Expand Down Expand Up @@ -72,7 +71,6 @@ func main() {
dataPath,
externalURL,
proxyURL,
globalSecret,
googleClientID,
googleClientSecret,
googleAllowedUsersList,
Expand All @@ -95,7 +93,6 @@ func main() {
rootCmd.Flags().StringVarP(&dataPath, "data", "d", getEnvWithDefault("DATA_PATH", "./data"), "Path to the data directory")
rootCmd.Flags().StringVarP(&externalURL, "external-url", "e", getEnvWithDefault("EXTERNAL_URL", "http://localhost"), "External URL for the proxy")
rootCmd.Flags().StringVarP(&proxyURL, "proxy-url", "p", getEnvWithDefault("PROXY_URL", "http://localhost:8080"), "Proxy URL for the proxy")
rootCmd.Flags().StringVarP(&globalSecret, "global-secret", "s", getEnvWithDefault("GLOBAL_SECRET", "supersecret"), "Global secret for the proxy")

// Google OAuth configuration
rootCmd.Flags().StringVar(&googleClientID, "google-client-id", getEnvWithDefault("GOOGLE_CLIENT_ID", ""), "Google OAuth client ID")
Expand Down
9 changes: 5 additions & 4 deletions pkg/mcp-proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package mcpproxy

import (
"context"
"crypto/sha256"
"errors"
"fmt"
"net/http"
Expand Down Expand Up @@ -37,7 +36,6 @@ func Run(
dataPath string,
externalURL string,
proxyURL string,
globalSecret string,
googleClientID string,
googleClientSecret string,
googleAllowedUsers []string,
Expand All @@ -54,8 +52,11 @@ func Run(
if parsedExternalURL.Path != "" {
return fmt.Errorf("external URL must not have a path, got: %s", parsedExternalURL.Path)
}
sha256Hash := sha256.Sum256([]byte(globalSecret))
secret := sha256Hash[:]

secret, err := utils.LoadOrGenerateSecret(path.Join(dataPath, "secret"))
if err != nil {
return fmt.Errorf("failed to load or generate secret: %w", err)
}

var config zap.Config
if os.Getenv("MODE") == "debug" {
Expand Down
24 changes: 24 additions & 0 deletions pkg/utils/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ import (
"os"
)

const SecretSize = 32

func LoadOrGenerateSecret(secretPath string) ([]byte, error) {
_, err := os.Stat(secretPath)
if os.IsNotExist(err) {
secret := make([]byte, SecretSize)
if _, err := rand.Read(secret); err != nil {
return nil, fmt.Errorf("failed to generate secret: %w", err)
}
if err := os.WriteFile(secretPath, secret, 0600); err != nil {
return nil, fmt.Errorf("failed to save secret: %w", err)
}
return secret, nil
}
if err != nil {
return nil, fmt.Errorf("failed to stat secret file: %w", err)
}
secret, err := os.ReadFile(secretPath)
if err != nil {
return nil, fmt.Errorf("failed to read secret file: %w", err)
}
return secret, nil
}

func LoadOrGeneratePrivateKey(keyPath string) (*rsa.PrivateKey, error) {
_, err := os.Stat(keyPath)
if os.IsNotExist(err) {
Expand Down