A Model Context Protocol (MCP) server implementation with OAuth2 authentication.
This server implements OAuth2 with the Authorization Code flow. The following endpoints are available:
POST /oauth/register
Content-Type: application/json
{
"username": "string",
"password": "string"
}
Response:
{
"message": "Registration successful",
"userId": "string"
}
POST /oauth/login
Content-Type: application/json
{
"username": "string",
"password": "string"
}
Response:
{
"message": "Login successful"
}
GET /oauth/authorize?client_id=client1&redirect_uri=http://localhost:3000/callback&response_type=code&state=random_state
Required parameters:
client_id
: The client identifier (currently only "client1" is supported)redirect_uri
: The callback URL where the user will be redirected after authorizationresponse_type
: Must be "code"state
: (Optional) A random string to prevent CSRF attacks
The server will redirect to the redirect_uri
with an authorization code:
http://localhost:3000/callback?code=AUTHORIZATION_CODE&state=random_state
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=http://localhost:3000/callback&
client_id=client1&
client_secret=secret1
Response:
{
"access_token": "string",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "string"
}
GET /oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
Response:
{
"id": "string",
"username": "string"
}
For command-line applications, you can implement the OAuth2 flow using manual code entry. Here's how:
- Open the authorization URL in the user's browser
- Let the user copy the authorization code from the callback URL
- Exchange the code for tokens
- Store the tokens securely
Example implementation in TypeScript:
import open from 'open'
import { randomBytes } from 'crypto'
import { config } from 'dotenv'
import readline from 'readline'
// Load environment variables
config()
const CLIENT_ID = 'client1'
const CLIENT_SECRET = 'secret1'
const REDIRECT_URI = 'http://localhost:3000/callback'
const AUTH_SERVER = 'http://localhost:3000'
async function authenticate() {
// Generate random state for CSRF protection
const state = randomBytes(16).toString('hex')
// Create readline interface for user input
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
// Open browser for authorization
const authUrl = new URL('/oauth/authorize', AUTH_SERVER)
authUrl.searchParams.set('client_id', CLIENT_ID)
authUrl.searchParams.set('redirect_uri', REDIRECT_URI)
authUrl.searchParams.set('response_type', 'code')
authUrl.searchParams.set('state', state)
console.log('Opening browser for authorization...')
await open(authUrl.toString())
// Wait for user to enter the authorization code
const code = await new Promise<string>(resolve => {
rl.question('Please enter the authorization code from the callback URL: ', resolve)
})
rl.close()
// Exchange code for tokens
const tokenResponse = await fetch(`${AUTH_SERVER}/oauth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET
})
})
const tokens = await tokenResponse.json()
return tokens
}
// Usage
async function main() {
try {
const tokens = await authenticate()
console.log('Access Token:', tokens.access_token)
console.log('Refresh Token:', tokens.refresh_token)
// Store tokens securely (e.g., in keychain or encrypted file)
// ...
} catch (error) {
console.error('Authentication failed:', error)
}
}
main()
To use this in your command-line application:
- Install dependencies:
npm install open dotenv
- Create a
.env
file:
CLIENT_ID=client1
CLIENT_SECRET=secret1
AUTH_SERVER=http://localhost:3000
- Run the authentication:
ts-node auth.ts
The script will:
- Open the user's browser to the authorization page
- Prompt the user to copy and paste the authorization code from the callback URL
- Exchange the code for access and refresh tokens
- Store the tokens securely
For subsequent API calls, use the access token:
async function makeApiCall(accessToken: string) {
const response = await fetch('http://localhost:3000/oauth/userinfo', {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
return response.json()
}
- Register a new user:
curl -X POST http://localhost:3000/oauth/register \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "password123"}'
- Login:
curl -X POST http://localhost:3000/oauth/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "password123"}'
- Start the OAuth flow by redirecting to:
http://localhost:3000/oauth/authorize?client_id=client1&redirect_uri=http://localhost:3000/callback&response_type=code
- Exchange the authorization code for tokens:
curl -X POST http://localhost:3000/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=http://localhost:3000/callback&client_id=client1&client_secret=secret1"
- Access protected resources:
curl http://localhost:3000/oauth/userinfo \
-H "Authorization: Bearer ACCESS_TOKEN"
- Node.js 18+
- Bun
bun install
bun run server.ts
The server will start on http://localhost:3000