Skip to content

feat: Atlassian MCP OAuth - #499

Merged
ct3685 merged 5 commits into
productionfrom
staging
Aug 21, 2025
Merged

feat: Atlassian MCP OAuth#499
ct3685 merged 5 commits into
productionfrom
staging

Conversation

@bradtaylorsf

Copy link
Copy Markdown
Collaborator

No description provided.

bradtaylorsf and others added 2 commits August 19, 2025 15:06
## Summary
Enhances the build process for the documentation package by adding a
script to fix the sitemap after building.

## Changes
- Updated the `build` script in `packages/docs/package.json` to include
`&& node scripts/fix-sitemap.js` after the Docusaurus build command.

This change ensures that the sitemap is properly fixed as part of the
build process, improving the documentation deployment workflow.
…efresh (AAI-530) (#443)

## Summary

Complete implementation of Atlassian MCP OAuth integration with seamless
automatic token refresh functionality for JIRA and Confluence access
through Model Context Protocol.

### Key Features
- **Atlassian MCP OAuth Integration**: Full OAuth 2.0 flow with dynamic
client registration
- **Automatic Token Refresh**: Seamless server-side token refresh before
node execution (5-minute buffer)
- **MCP Server Support**: Integration with Atlassian's remote MCP server
at https://mcp.atlassian.com/v1/sse
- **Clean Architecture**: No circular imports, proper database access
patterns
- **User Transparency**: Token refresh happens behind the scenes - users
are "none the wiser"

### Components Added
- `AtlassianOauth.credential.ts`: OAuth credential definition with MCP
client fields
- `AtlassianMcp.ts`: MCP node for JIRA/Confluence tool integration
- `checkAndRefreshCredentialsBeforeInit()`: Server-side
pre-initialization token refresh hook
- OAuth controllers and routes for authentication flow
- MCP metadata utilities with discovery support

### Technical Implementation
- **Pre-initialization Hook**: Automatically refreshes tokens before
node execution in buildFlow
- **Proper Content-Type**: Uses `application/x-www-form-urlencoded` for
MCP token endpoints
- **Database Integration**: Persists refreshed tokens using TypeORM
repositories
- **Error Handling**: Graceful fallbacks that don't break flow execution
- **Clean Dependencies**: Uses function parameters to avoid circular
imports

### Testing
- ✅ Token refresh logic tested and verified working
- ✅ MCP server connection established successfully
- ✅ OAuth flow completed end-to-end
- ✅ TypeScript compilation clean
- ✅ Server starts without errors

Fixes AAI-530: Enable MCP server authentication for Atlassian
integration

## Test Plan
- [x] OAuth authentication flow works end-to-end
- [x] Token refresh triggers automatically when needed
- [x] MCP tools load and execute successfully  
- [x] Tokens persist correctly to database
- [x] Error handling works gracefully
- [x] TypeScript compiles without errors
- [x] Server starts and runs without issues
@vercel

vercel Bot commented Aug 21, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
answerai-docs Ready Ready Preview Aug 21, 2025 5:23pm
the-answerai Ready Ready Preview Aug 21, 2025 5:23pm

const router = express.Router()

// GET /api/v1/atlassian-auth/
router.get('/', atlassianAuthController.authenticate)

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI about 1 year ago

To resolve this issue, rate-limiting middleware should be added to the affected route(s), specifically the /api/v1/atlassian-auth/ GET handler. The preferred approach is to use a well-established package such as express-rate-limit. You will need to:

  • Install the express-rate-limit package.
  • Import express-rate-limit in the file.
  • Create a rate limiter instance (e.g., allowing a reasonable number of authentication requests per IP per time window).
  • Apply the rate limiter as middleware to the route for atlassianAuthController.authenticate.
  • Make minimal changes that avoid altering any existing route logic or structure.

This fix should be made directly in packages/server/src/routes/atlassian-auth/index.ts near the affected route.


Suggested changeset 1
packages/server/src/routes/atlassian-auth/index.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/server/src/routes/atlassian-auth/index.ts b/packages/server/src/routes/atlassian-auth/index.ts
--- a/packages/server/src/routes/atlassian-auth/index.ts
+++ b/packages/server/src/routes/atlassian-auth/index.ts
@@ -1,11 +1,18 @@
 import express from 'express'
 import passport from 'passport'
 import atlassianAuthController from '../../controllers/atlassian-auth'
+import rateLimit from 'express-rate-limit'
 
+const authRateLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 100, // Limit each IP to 100 requests per windowMs
+  message: 'Too many authentication attempts from this IP, please try again later.'
+})
+
 const router = express.Router()
 
 // GET /api/v1/atlassian-auth/
-router.get('/', atlassianAuthController.authenticate)
+router.get('/', authRateLimiter, atlassianAuthController.authenticate)
 
 // GET /api/v1/atlassian-auth/callback
 router.get('/callback', passport.authenticate('atlassian-dynamic', { session: false }), atlassianAuthController.atlassianAuthCallback)
EOF
@@ -1,11 +1,18 @@
import express from 'express'
import passport from 'passport'
import atlassianAuthController from '../../controllers/atlassian-auth'
import rateLimit from 'express-rate-limit'

const authRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many authentication attempts from this IP, please try again later.'
})

const router = express.Router()

// GET /api/v1/atlassian-auth/
router.get('/', atlassianAuthController.authenticate)
router.get('/', authRateLimiter, atlassianAuthController.authenticate)

// GET /api/v1/atlassian-auth/callback
router.get('/callback', passport.authenticate('atlassian-dynamic', { session: false }), atlassianAuthController.atlassianAuthCallback)
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
router.get('/callback', passport.authenticate('atlassian-dynamic', { session: false }), atlassianAuthController.atlassianAuthCallback)

// GET /api/v1/atlassian-auth/mcp-initialize
router.get('/mcp-initialize', atlassianAuthController.mcpInitialize)

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI about 1 year ago

To address this issue, we should apply rate-limiting middleware to the /mcp-initialize route to prevent abuse. The standard approach in an Express app is to use a middleware like express-rate-limit. This involves:

  • Importing express-rate-limit at the top of the file.
  • Creating a new rate limiter instance with appropriate configuration (e.g., 100 requests per 15 minutes per IP).
  • Applying this rate limiter only to the /mcp-initialize route in the router.

All the modifications will be isolated to packages/server/src/routes/atlassian-auth/index.ts. No other files are modified.


Suggested changeset 1
packages/server/src/routes/atlassian-auth/index.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/server/src/routes/atlassian-auth/index.ts b/packages/server/src/routes/atlassian-auth/index.ts
--- a/packages/server/src/routes/atlassian-auth/index.ts
+++ b/packages/server/src/routes/atlassian-auth/index.ts
@@ -1,9 +1,16 @@
 import express from 'express'
 import passport from 'passport'
 import atlassianAuthController from '../../controllers/atlassian-auth'
-
+import rateLimit from 'express-rate-limit'
 const router = express.Router()
 
+// Rate limiter for mcp-initialize route (e.g., max 100 requests per 15 minutes per IP)
+const mcpInitializeLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 100, // limit each IP to 100 requests per windowMs
+  standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
+  legacyHeaders: false, // Disable the `X-RateLimit-*` headers
+})
 // GET /api/v1/atlassian-auth/
 router.get('/', atlassianAuthController.authenticate)
 
@@ -11,6 +16,6 @@
 router.get('/callback', passport.authenticate('atlassian-dynamic', { session: false }), atlassianAuthController.atlassianAuthCallback)
 
 // GET /api/v1/atlassian-auth/mcp-initialize
-router.get('/mcp-initialize', atlassianAuthController.mcpInitialize)
+router.get('/mcp-initialize', mcpInitializeLimiter, atlassianAuthController.mcpInitialize)
 
 export default router
EOF
@@ -1,9 +1,16 @@
import express from 'express'
import passport from 'passport'
import atlassianAuthController from '../../controllers/atlassian-auth'

import rateLimit from 'express-rate-limit'
const router = express.Router()

// Rate limiter for mcp-initialize route (e.g., max 100 requests per 15 minutes per IP)
const mcpInitializeLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
})
// GET /api/v1/atlassian-auth/
router.get('/', atlassianAuthController.authenticate)

@@ -11,6 +16,6 @@
router.get('/callback', passport.authenticate('atlassian-dynamic', { session: false }), atlassianAuthController.atlassianAuthCallback)

// GET /api/v1/atlassian-auth/mcp-initialize
router.get('/mcp-initialize', atlassianAuthController.mcpInitialize)
router.get('/mcp-initialize', mcpInitializeLimiter, atlassianAuthController.mcpInitialize)

export default router
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
router.post('/refresh-token', enforceAbility('Credential'), credentialsController.updateAndRefreshToken)

// UPDATE REFRESH ATLASSIAN TOKEN
router.post('/refresh-atlassian-token', enforceAbility('Credential'), credentialsController.updateAndRefreshAtlassianToken)

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI about 1 year ago

The best way to fix this issue is to add a rate limiting middleware to sensitive routes such as /refresh-atlassian-token. A widely accepted solution is to use the express-rate-limit package, which fits Express applications and can be configured per route as needed. We should:

  • Import express-rate-limit.
  • Define a rate limiter instance with sensible defaults (e.g., max requests per time window for this endpoint).
  • Apply this rate limiter as the first middleware for the route /refresh-atlassian-token.

All changes must be in packages/server/src/routes/credentials/index.ts. We will need to:

  • Import express-rate-limit.
  • Add the rate limiter definition.
  • Add it as middleware for the /refresh-atlassian-token route.

Suggested changeset 1
packages/server/src/routes/credentials/index.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/server/src/routes/credentials/index.ts b/packages/server/src/routes/credentials/index.ts
--- a/packages/server/src/routes/credentials/index.ts
+++ b/packages/server/src/routes/credentials/index.ts
@@ -1,9 +1,17 @@
 import express from 'express'
 import credentialsController from '../../controllers/credentials'
 import enforceAbility from '../../middlewares/authentication/enforceAbility'
+import rateLimit from 'express-rate-limit'
 
 const router = express.Router()
 
+// Rate limiter for sensitive routes
+const refreshAtlassianTokenRateLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 20, // limit each IP to 20 requests per windowMs (adjust as reasonable)
+  message: 'Too many requests, please try again later.',
+})
+
 // CREATE
 router.post('/', enforceAbility('Credential'), credentialsController.createCredential)
 
@@ -21,6 +26,11 @@
 router.post('/refresh-token', enforceAbility('Credential'), credentialsController.updateAndRefreshToken)
 
 // UPDATE REFRESH ATLASSIAN TOKEN
-router.post('/refresh-atlassian-token', enforceAbility('Credential'), credentialsController.updateAndRefreshAtlassianToken)
+router.post(
+  '/refresh-atlassian-token',
+  refreshAtlassianTokenRateLimiter,
+  enforceAbility('Credential'),
+  credentialsController.updateAndRefreshAtlassianToken
+)
 
 export default router
EOF
@@ -1,9 +1,17 @@
import express from 'express'
import credentialsController from '../../controllers/credentials'
import enforceAbility from '../../middlewares/authentication/enforceAbility'
import rateLimit from 'express-rate-limit'

const router = express.Router()

// Rate limiter for sensitive routes
const refreshAtlassianTokenRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 20, // limit each IP to 20 requests per windowMs (adjust as reasonable)
message: 'Too many requests, please try again later.',
})

// CREATE
router.post('/', enforceAbility('Credential'), credentialsController.createCredential)

@@ -21,6 +26,11 @@
router.post('/refresh-token', enforceAbility('Credential'), credentialsController.updateAndRefreshToken)

// UPDATE REFRESH ATLASSIAN TOKEN
router.post('/refresh-atlassian-token', enforceAbility('Credential'), credentialsController.updateAndRefreshAtlassianToken)
router.post(
'/refresh-atlassian-token',
refreshAtlassianTokenRateLimiter,
enforceAbility('Credential'),
credentialsController.updateAndRefreshAtlassianToken
)

export default router
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
# feat: add automated staging reset workflow

## Summary
Implements a GitHub Action that automatically resets staging to match
production after staging-to-production releases, preventing the 90+ file
change issue that occurs when staging accumulates commits.

## Changes
- **Smart Staging Reset Action**: Automatically resets staging only when
staging→production PRs are merged
- **Simplified Logic**: Removes complex safety checks that would prevent
the action from working
- **Hotfix Protection**: Does not trigger on hotfix PRs
(feature→production)
- **Documentation**: Added comments explaining the workflow purpose

## Technical Details
- Triggers on PR close events targeting production branch
- Only executes when source branch is 'staging'
- Resets staging when it has commits ahead of production
- Uses --force-with-lease for safe force pushing
- Provides detailed logging and status messages

## Benefits
- Eliminates manual staging resets
- Prevents 90+ file change issues in future PRs
- Maintains clean git workflow
- Safe for hotfixes and normal development
- Works with active test branch workflow

## Testing
This workflow will be tested when this PR is merged to staging, and will
activate on the next staging→production release.

---------

Co-authored-by: Bradley Taylor <bradtaylorsf@gmail.com>
@ct3685
ct3685 self-requested a review August 21, 2025 17:33
ct3685
ct3685 previously approved these changes Aug 21, 2025

@ct3685 ct3685 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

## Summary
Introduces a new `robots.txt` file to enhance SEO by allowing all
crawlers to access the site's content and specifying the sitemap
location.

## Changes
- Created `robots.txt` in `packages/docs/static/` with rules for web
crawlers.
- Added directives to allow all user agents and provide the sitemap URL.

This addition aims to improve search engine indexing and visibility for
the site.
@sonarqubecloud

Copy link
Copy Markdown

@ct3685 ct3685 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ct3685
ct3685 merged commit 96b3b16 into production Aug 21, 2025
8 of 10 checks passed
@maxtechera
maxtechera temporarily deployed to staging - theanswer-iek0 August 21, 2025 18:25 — with Render Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature github_actions Pull requests that update GitHub Actions code MCP patch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants