A simple, reusable Node.js implementation of OAuth 2.0 authentication for Atlassian products (Jira, Confluence, etc.) using the Authorization Code flow with PKCE.
- 🔐 Secure OAuth 2.0 authentication with PKCE
- 🎯 Automatic cloud ID detection and storage
- 📦 Ready-to-use Jira API integration
- 🍪 Session management with HTTP-only cookies
- 🚀 Minimal dependencies and easy setup
- 🔄 Support for multiple Atlassian sites per user
- Node.js (v14 or higher)
- npm or yarn
- An Atlassian account
- Access to Atlassian Developer Console
git clone https://github.com/yourusername/atlassian-oauth.git
cd atlassian-oauthnpm install- Go to Atlassian Developer Console
- Click "Create" → "OAuth 2.0 integration"
- Fill in the app details:
- App name: Your app name
- App description: Brief description
- Company: Your company name
- Set up OAuth 2.0:
- Callback URL:
http://localhost:3978/auth/callback|| your production url. - Permissions: Configure the following scopes:
read:jira-user- View Jira user informationread:jira-work- View Jira projects and issuesoffline_access- Maintain access to dataread:me- View user profile
- Callback URL:
- Save your app and note down:
- Client ID
- Client Secret
Create a .env file in the project root:
# Required
ATLASSIAN_CLIENT_ID=your-client-id-here
ATLASSIAN_CLIENT_SECRET=your-client-secret-here
# Optional (these have defaults)
PORT=3978
ATLASSIAN_REDIRECT_URI=http://localhost:3978/auth/callbacknpm startVisit http://localhost:3978 and click "Login with Atlassian" to test the authentication flow.
atlassian-oauth/
├── app.js # Main Express application
├── auth.js # OAuth authentication logic
├── config.js # Configuration management
├── jiraApi.js # Jira API integration
├── package.json # Project dependencies
├── .env # Environment variables (create this)
└── README.md # This file
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Home page with login status |
/auth/login |
GET | Initiates OAuth flow |
/auth/callback |
GET | OAuth callback handler |
/auth/logout |
GET | Clears session and logs out |
/api/me |
GET | Returns user profile and available sites |
/api/projects |
GET | Lists all Jira projects |
After authentication, you can get user information:
// In your route handler
const { getUserProfile } = require('./auth');
app.get('/api/user-info', async (req, res) => {
try {
const profile = await getUserProfile(req);
res.json(profile);
} catch (error) {
res.status(401).json({ error: 'Not authenticated' });
}
});const { getProjects } = require('./jiraApi');
app.get('/api/my-projects', async (req, res) => {
try {
const projects = await getProjects(req);
res.json(projects);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch projects' });
}
});const { createIssue } = require('./jiraApi');
app.post('/api/create-issue', async (req, res) => {
try {
const issueData = {
fields: {
project: { key: 'PROJ' },
summary: 'New issue from OAuth app',
description: 'Issue description',
issuetype: { name: 'Task' }
}
};
const issue = await createIssue(req, issueData);
res.json(issue);
} catch (error) {
res.status(500).json({ error: 'Failed to create issue' });
}
});-
Authentication Flow:
- User clicks "Login with Atlassian"
- App redirects to Atlassian's OAuth authorization page
- User approves permissions
- Atlassian redirects back with authorization code
- App exchanges code for access token
- Token and cloud ID are stored in secure cookies
-
Session Management:
- Access tokens are stored in HTTP-only cookies
- Sessions expire after 1 hour
- Cloud ID is automatically detected and stored
-
API Access:
- All Jira API calls automatically use the stored cloud ID
- Authentication headers are automatically included
- 🔒 Uses PKCE (Proof Key for Code Exchange) for enhanced security
- 🍪 Stores tokens in HTTP-only cookies to prevent XSS attacks
- 🚫 Never exposes client secrets to the frontend
- ⏰ Implements proper session expiration
- 🔐 All sensitive data is handled server-side only
To request additional permissions, modify the scopes in auth.js:
const scope = 'read:jira-user read:jira-work offline_access read:me write:jira-work';Add Confluence-specific endpoints in a new file confluenceApi.js:
const getSpaces = async (req) => {
const accessToken = getAccessToken(req);
const cloudId = getCloudId(req);
const response = await axios.get(
`https://api.atlassian.com/ex/confluence/${cloudId}/rest/api/space`,
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'Accept': 'application/json'
}
}
);
return response.data;
};-
"Invalid redirect URI" error:
- Ensure the callback URL in your
.envmatches exactly with the one in Atlassian Developer Console
- Ensure the callback URL in your
-
"401 Unauthorized" errors:
- Check if your access token has expired
- Verify that you have the required scopes
-
Cannot access Jira projects:
- Ensure your Atlassian account has access to at least one Jira site
- Check that the
read:jira-workscope is enabled
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
If you encounter any issues or have questions:
- Open an issue on GitHub
- Check the Atlassian OAuth documentation
- Review the CLAUDE.md file for detailed development notes
- Built with Express.js
- Uses Atlassian's OAuth 2.0 (3LO) implementation
- Inspired by the need for a simple, reusable Atlassian authentication solution
Since the OAuth authentication flow is the same for all Atlassian products, you can easily extend this project to support Confluence API calls. Simply create a confluenceApi.js file similar to jiraApi.js and use the same authentication tokens and helper functions from auth.js. The access token and cloud ID obtained during login work across all Atlassian products (Jira, Confluence, Trello, etc.) that the user has access to.