-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth.js
More file actions
154 lines (132 loc) · 4.31 KB
/
Copy pathauth.js
File metadata and controls
154 lines (132 loc) · 4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import axios from 'axios';
import crypto from 'crypto';
import config from './config.js';
let codeVerifier;
let codeChallenge;
export function initAuth(app) {
// Generate PKCE parameters
codeVerifier = crypto.randomBytes(32).toString('base64url');
codeChallenge = crypto
.createHash('sha256')
.update(codeVerifier)
.digest('base64url');
// Login route
app.get('/auth/login', (req, res) => {
const authParams = new URLSearchParams({
audience: 'api.atlassian.com',
client_id: config.ATLASSIAN.CLIENT_ID,
scope: 'read:jira-user read:jira-work offline_access read:me',
redirect_uri: config.ATLASSIAN.REDIRECT_URI,
state: crypto.randomBytes(16).toString('hex'),
response_type: 'code',
prompt: 'consent',
code_challenge: codeChallenge,
code_challenge_method: 'S256'
});
const authUrl = `https://auth.atlassian.com/authorize?${authParams}`;
res.redirect(authUrl);
});
// Callback route
app.get('/auth/callback', async (req, res) => {
const { code } = req.query;
if (!code) {
return res.status(400).send('Authorization code not found');
}
try {
// Exchange code for tokens
const tokenResponse = await axios.post('https://auth.atlassian.com/oauth/token', {
grant_type: 'authorization_code',
client_id: config.ATLASSIAN.CLIENT_ID,
client_secret: config.ATLASSIAN.CLIENT_SECRET,
code: code,
redirect_uri: config.ATLASSIAN.REDIRECT_URI,
code_verifier: codeVerifier
});
const { access_token, refresh_token } = tokenResponse.data;
// Store token in cookie
res.cookie('access_token', access_token, {
httpOnly: true,
secure: false, // set to true in production
sameSite: 'lax',
maxAge: 3600000 // 1 hour
});
// Get and store cloud ID automatically
try {
const sitesResponse = await axios.get('https://api.atlassian.com/oauth/token/accessible-resources', {
headers: { 'Authorization': `Bearer ${access_token}` }
});
if (sitesResponse.data && sitesResponse.data.length > 0) {
const defaultSite = sitesResponse.data[0];
// Store cloud ID and site info in cookies
res.cookie('cloud_id', defaultSite.id, {
httpOnly: true,
secure: false,
sameSite: 'lax',
maxAge: 3600000
});
res.cookie('site_url', defaultSite.url, {
httpOnly: true,
secure: false,
sameSite: 'lax',
maxAge: 3600000
});
res.cookie('site_name', defaultSite.name, {
httpOnly: true,
secure: false,
sameSite: 'lax',
maxAge: 3600000
});
}
} catch (error) {
console.error('Error fetching cloud ID:', error);
}
// Redirect to home page
res.redirect('/');
} catch (error) {
console.error('Error in auth callback:', error.response?.data || error.message);
res.status(500).send('Authentication failed');
}
});
}
export async function getAccessToken(req) {
if (req && req.cookies && req.cookies.access_token) {
return req.cookies.access_token;
}
throw new Error('Not authenticated');
}
export async function getCloudId(req) {
if (req && req.cookies && req.cookies.cloud_id) {
return req.cookies.cloud_id;
}
throw new Error('Cloud ID not found');
}
export async function getSiteInfo(req) {
if (req && req.cookies) {
return {
cloudId: req.cookies.cloud_id,
url: req.cookies.site_url,
name: req.cookies.site_name
};
}
throw new Error('Site info not found');
}
export async function getUserProfile(req) {
const token = await getAccessToken(req);
try {
// Get user profile
const profileResponse = await axios.get('https://api.atlassian.com/me', {
headers: { 'Authorization': `Bearer ${token}` }
});
// Get accessible Jira sites
const sitesResponse = await axios.get('https://api.atlassian.com/oauth/token/accessible-resources', {
headers: { 'Authorization': `Bearer ${token}` }
});
return {
user: profileResponse.data,
sites: sitesResponse.data
};
} catch (error) {
console.error('Error fetching user profile:', error);
throw error;
}
}