@manojkmfsi/monodog 1.1.18 → 1.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +19 -0
- package/CHANGELOG.md +6 -0
- package/dist/middleware/auth-middleware.js +186 -0
- package/dist/middleware/server-startup.js +25 -0
- package/dist/routes/auth-routes.js +342 -0
- package/dist/routes/permission-routes.js +161 -0
- package/dist/serve.js +8 -2
- package/dist/services/github-oauth-service.js +223 -0
- package/dist/services/permission-service.js +174 -0
- package/dist/types/auth.js +5 -0
- package/monodog-dashboard/dist/assets/index-DN0rk9Ub.css +1 -0
- package/monodog-dashboard/dist/assets/index-DS89XTlx.js +12 -0
- package/monodog-dashboard/dist/index.html +2 -2
- package/package.json +3 -1
- package/monodog-dashboard/dist/assets/index-BCgtRT1v.css +0 -1
- package/monodog-dashboard/dist/assets/index-x6HJzHWK.js +0 -12
package/.env.example
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# GitHub OAuth Configuration
|
|
2
|
+
GITHUB_CLIENT_ID=your_client_id_here
|
|
3
|
+
GITHUB_CLIENT_SECRET=your_client_secret_here
|
|
4
|
+
OAUTH_REDIRECT_URI=http://localhost:3000/auth/callback
|
|
5
|
+
|
|
6
|
+
# Database Configuration
|
|
7
|
+
# DATABASE_URL=postgresql://user:password@localhost:5432/monodog
|
|
8
|
+
|
|
9
|
+
# # Server Configuration
|
|
10
|
+
# SERVER_HOST=localhost
|
|
11
|
+
# SERVER_PORT=5000
|
|
12
|
+
# DASHBOARD_HOST=localhost
|
|
13
|
+
# DASHBOARD_PORT=3000
|
|
14
|
+
|
|
15
|
+
# # Logging Level (debug, info, warn, error)
|
|
16
|
+
# LOG_LEVEL=info
|
|
17
|
+
|
|
18
|
+
# # Environment
|
|
19
|
+
# NODE_ENV=development
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @manojkmfsi/monoapp
|
|
2
2
|
|
|
3
|
+
## 1.1.19
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#146](https://github.com/manojkmfsi/monodog/pull/146) [`5419367`](https://github.com/manojkmfsi/monodog/commit/54193676da5c07498a6dfbbc2bb62a53072648ca) Thanks [@manojkmfsi](https://github.com/manojkmfsi)! - km..,/.,/.
|
|
8
|
+
|
|
3
9
|
## 1.1.18
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Authentication Middleware
|
|
4
|
+
* Handles session validation and permission checks
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.generateSessionToken = generateSessionToken;
|
|
8
|
+
exports.storeSession = storeSession;
|
|
9
|
+
exports.getSession = getSession;
|
|
10
|
+
exports.invalidateSession = invalidateSession;
|
|
11
|
+
exports.authenticationMiddleware = authenticationMiddleware;
|
|
12
|
+
exports.repositoryPermissionMiddleware = repositoryPermissionMiddleware;
|
|
13
|
+
exports.getSessionFromRequest = getSessionFromRequest;
|
|
14
|
+
exports.isAuthenticated = isAuthenticated;
|
|
15
|
+
exports.clearExpiredSessions = clearExpiredSessions;
|
|
16
|
+
exports.getSessionStats = getSessionStats;
|
|
17
|
+
exports.initializeAuthentication = initializeAuthentication;
|
|
18
|
+
const logger_1 = require("./logger");
|
|
19
|
+
// Store sessions in memory (should be replaced with proper session store in production)
|
|
20
|
+
const sessionStore = new Map();
|
|
21
|
+
const sessionTimeout = 24 * 60 * 60 * 1000; // 24 hours
|
|
22
|
+
/**
|
|
23
|
+
* Generate session token
|
|
24
|
+
*/
|
|
25
|
+
function generateSessionToken(length = 32) {
|
|
26
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
27
|
+
let token = '';
|
|
28
|
+
for (let i = 0; i < length; i++) {
|
|
29
|
+
token += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
30
|
+
}
|
|
31
|
+
return token;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Store session
|
|
35
|
+
*/
|
|
36
|
+
function storeSession(session) {
|
|
37
|
+
const token = generateSessionToken();
|
|
38
|
+
sessionStore.set(token, session);
|
|
39
|
+
// Set expiration timeout
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
sessionStore.delete(token);
|
|
42
|
+
logger_1.AppLogger.debug(`Session expired: ${session.user.login}`);
|
|
43
|
+
}, sessionTimeout);
|
|
44
|
+
logger_1.AppLogger.debug(`Session stored for user: ${session.user.login}`);
|
|
45
|
+
return token;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get session by token
|
|
49
|
+
*/
|
|
50
|
+
function getSession(token) {
|
|
51
|
+
const session = sessionStore.get(token);
|
|
52
|
+
if (!session) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// Check if session has expired based on expiresAt
|
|
56
|
+
if (Date.now() > session.expiresAt) {
|
|
57
|
+
sessionStore.delete(token);
|
|
58
|
+
logger_1.AppLogger.warn(`Session token expired: ${token.substring(0, 10)}...`);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return session;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Invalidate session
|
|
65
|
+
*/
|
|
66
|
+
function invalidateSession(token) {
|
|
67
|
+
sessionStore.delete(token);
|
|
68
|
+
logger_1.AppLogger.debug(`Session invalidated: ${token.substring(0, 10)}...`);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Middleware to verify authentication
|
|
72
|
+
*/
|
|
73
|
+
function authenticationMiddleware(req, res, next) {
|
|
74
|
+
const token = req.headers.authorization?.replace('Bearer ', '') ||
|
|
75
|
+
req.cookies?.['auth-token'];
|
|
76
|
+
if (!token) {
|
|
77
|
+
logger_1.AppLogger.warn(`Unauthorized request to ${req.path}: no auth token`);
|
|
78
|
+
res.status(401).json({
|
|
79
|
+
error: 'Unauthorized',
|
|
80
|
+
message: 'Authentication token required',
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const session = getSession(token);
|
|
85
|
+
if (!session) {
|
|
86
|
+
logger_1.AppLogger.warn(`Unauthorized request to ${req.path}: invalid session`);
|
|
87
|
+
res.status(401).json({
|
|
88
|
+
error: 'Unauthorized',
|
|
89
|
+
message: 'Invalid or expired session',
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Attach session to request
|
|
94
|
+
req.session = session;
|
|
95
|
+
logger_1.AppLogger.debug(`Authenticated request from user: ${session.user.login}`);
|
|
96
|
+
next();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Middleware to verify repository permission
|
|
100
|
+
*/
|
|
101
|
+
function repositoryPermissionMiddleware(requiredPermission) {
|
|
102
|
+
return (req, res, next) => {
|
|
103
|
+
const authReq = req;
|
|
104
|
+
const session = authReq.session;
|
|
105
|
+
if (!session) {
|
|
106
|
+
res.status(401).json({
|
|
107
|
+
error: 'Unauthorized',
|
|
108
|
+
message: 'Session not found',
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const permission = authReq.permission;
|
|
113
|
+
if (!permission) {
|
|
114
|
+
res.status(403).json({
|
|
115
|
+
error: 'Forbidden',
|
|
116
|
+
message: 'Permission not resolved for repository',
|
|
117
|
+
});
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const permissionHierarchy = {
|
|
121
|
+
admin: 4,
|
|
122
|
+
maintain: 3,
|
|
123
|
+
write: 2,
|
|
124
|
+
read: 1,
|
|
125
|
+
none: 0,
|
|
126
|
+
};
|
|
127
|
+
const userLevel = permissionHierarchy[permission.permission] || 0;
|
|
128
|
+
const requiredLevel = permissionHierarchy[requiredPermission] || 0;
|
|
129
|
+
if (userLevel < requiredLevel) {
|
|
130
|
+
logger_1.AppLogger.warn(`User ${session.user.login} lacks permission for action requiring ${requiredPermission}`);
|
|
131
|
+
res.status(403).json({
|
|
132
|
+
error: 'Forbidden',
|
|
133
|
+
message: `This action requires ${requiredPermission} permission`,
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
next();
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get session from request
|
|
142
|
+
*/
|
|
143
|
+
function getSessionFromRequest(req) {
|
|
144
|
+
return req.session || null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check if user is authenticated
|
|
148
|
+
*/
|
|
149
|
+
function isAuthenticated(req) {
|
|
150
|
+
return !!getSessionFromRequest(req);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Clear expired sessions (should be called periodically)
|
|
154
|
+
*/
|
|
155
|
+
function clearExpiredSessions() {
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
let clearedCount = 0;
|
|
158
|
+
for (const [token, session] of sessionStore.entries()) {
|
|
159
|
+
if (now > session.expiresAt) {
|
|
160
|
+
sessionStore.delete(token);
|
|
161
|
+
clearedCount++;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (clearedCount > 0) {
|
|
165
|
+
logger_1.AppLogger.debug(`Cleared ${clearedCount} expired sessions`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get session statistics
|
|
170
|
+
*/
|
|
171
|
+
function getSessionStats() {
|
|
172
|
+
return {
|
|
173
|
+
activeSessions: sessionStore.size,
|
|
174
|
+
maxSessions: 10000,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Initialize authentication (call on server startup)
|
|
179
|
+
*/
|
|
180
|
+
function initializeAuthentication() {
|
|
181
|
+
// Periodically clear expired sessions
|
|
182
|
+
setInterval(() => {
|
|
183
|
+
clearExpiredSessions();
|
|
184
|
+
}, 60 * 1000); // Every minute
|
|
185
|
+
logger_1.AppLogger.info('Authentication system initialized');
|
|
186
|
+
}
|
|
@@ -18,7 +18,11 @@ const package_routes_1 = __importDefault(require("../routes/package-routes"));
|
|
|
18
18
|
const commit_routes_1 = __importDefault(require("../routes/commit-routes"));
|
|
19
19
|
const health_routes_1 = __importDefault(require("../routes/health-routes"));
|
|
20
20
|
const config_routes_1 = __importDefault(require("../routes/config-routes"));
|
|
21
|
+
const auth_routes_1 = __importDefault(require("../routes/auth-routes"));
|
|
22
|
+
const permission_routes_1 = __importDefault(require("../routes/permission-routes"));
|
|
21
23
|
const constants_1 = require("../constants");
|
|
24
|
+
const auth_middleware_1 = require("./auth-middleware");
|
|
25
|
+
const permission_service_1 = require("../services/permission-service");
|
|
22
26
|
/**
|
|
23
27
|
* Validate port number
|
|
24
28
|
*/
|
|
@@ -49,7 +53,13 @@ function createApp(rootPath) {
|
|
|
49
53
|
app.use(logger_1.httpLogger);
|
|
50
54
|
// Setup Swagger documentation
|
|
51
55
|
(0, swagger_middleware_1.setupSwaggerDocs)(app);
|
|
56
|
+
// Initialize authentication system
|
|
57
|
+
(0, auth_middleware_1.initializeAuthentication)();
|
|
58
|
+
// Start permission cache cleanup
|
|
59
|
+
(0, permission_service_1.startCacheCleanup)();
|
|
52
60
|
// Routes
|
|
61
|
+
app.use('/api/auth', auth_routes_1.default);
|
|
62
|
+
app.use('/api/permissions', permission_routes_1.default);
|
|
53
63
|
app.use('/api/packages', package_routes_1.default);
|
|
54
64
|
app.use('/api/commits/', commit_routes_1.default);
|
|
55
65
|
app.use('/api/health/', health_routes_1.default);
|
|
@@ -75,13 +85,28 @@ function startServer(rootPath) {
|
|
|
75
85
|
console.log((0, constants_1.SUCCESS_SERVER_START)(host, validatedPort));
|
|
76
86
|
logger_1.AppLogger.info('API endpoints available:', {
|
|
77
87
|
endpoints: [
|
|
88
|
+
// Auth endpoints
|
|
89
|
+
'GET /api/auth/login',
|
|
90
|
+
'GET /api/auth/callback',
|
|
91
|
+
'GET /api/auth/me',
|
|
92
|
+
'POST /api/auth/validate',
|
|
93
|
+
'POST /api/auth/logout',
|
|
94
|
+
'POST /api/auth/refresh',
|
|
95
|
+
// Permission endpoints
|
|
96
|
+
'GET /api/permissions/:owner/:repo',
|
|
97
|
+
'POST /api/permissions/:owner/:repo/can-action',
|
|
98
|
+
'POST /api/permissions/:owner/:repo/invalidate',
|
|
99
|
+
// Package endpoints
|
|
78
100
|
'POST /api/packages/refresh',
|
|
79
101
|
'GET /api/packages',
|
|
80
102
|
'GET /api/packages/:name',
|
|
81
103
|
'PUT /api/packages/update-config',
|
|
104
|
+
// Commit endpoints
|
|
82
105
|
'GET /api/commits/:packagePath',
|
|
106
|
+
// Health endpoints
|
|
83
107
|
'GET /api/health/packages',
|
|
84
108
|
'POST /api/health/refresh',
|
|
109
|
+
// Config endpoints
|
|
85
110
|
'PUT /api/config/files/:id',
|
|
86
111
|
'GET /api/config/files',
|
|
87
112
|
],
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Authentication Routes
|
|
4
|
+
* Handles GitHub OAuth and session management
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const express_1 = require("express");
|
|
8
|
+
const github_oauth_service_1 = require("../services/github-oauth-service");
|
|
9
|
+
const auth_middleware_1 = require("../middleware/auth-middleware");
|
|
10
|
+
const logger_1 = require("../middleware/logger");
|
|
11
|
+
const router = (0, express_1.Router)();
|
|
12
|
+
// OAuth configuration (should come from environment variables)
|
|
13
|
+
// const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID || '';
|
|
14
|
+
// const GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET || '';
|
|
15
|
+
// const OAUTH_REDIRECT_URI = process.env.OAUTH_REDIRECT_URI || 'http://localhost:3000/auth/callback';
|
|
16
|
+
// State store for CSRF protection
|
|
17
|
+
const stateStore = new Map();
|
|
18
|
+
const STATE_EXPIRY = 10 * 60 * 1000; // 10 minutes
|
|
19
|
+
/**
|
|
20
|
+
* Generate random state for CSRF protection
|
|
21
|
+
*/
|
|
22
|
+
function generateState() {
|
|
23
|
+
return Math.random().toString(36).substring(2, 15) +
|
|
24
|
+
Math.random().toString(36).substring(2, 15);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate OAuth state
|
|
28
|
+
*/
|
|
29
|
+
function validateState(state) {
|
|
30
|
+
const entry = stateStore.get(state);
|
|
31
|
+
if (!entry) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
// Check if state has expired
|
|
35
|
+
if (Date.now() - entry.createdAt > STATE_EXPIRY) {
|
|
36
|
+
stateStore.delete(state);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get redirect URL from state
|
|
43
|
+
*/
|
|
44
|
+
function getRedirectUrl(state) {
|
|
45
|
+
const entry = stateStore.get(state);
|
|
46
|
+
return entry?.redirectUrl;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Clear state after use
|
|
50
|
+
*/
|
|
51
|
+
function clearState(state) {
|
|
52
|
+
stateStore.delete(state);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Start OAuth login flow
|
|
56
|
+
* GET /auth/login
|
|
57
|
+
*/
|
|
58
|
+
router.get('/login', (req, res) => {
|
|
59
|
+
try {
|
|
60
|
+
if (!process.env.GITHUB_CLIENT_ID) {
|
|
61
|
+
logger_1.AppLogger.error('GitHub client ID not configured');
|
|
62
|
+
res.status(500).json({
|
|
63
|
+
error: 'OAuth not configured',
|
|
64
|
+
message: 'GitHub OAuth is not properly configured',
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const state = generateState();
|
|
69
|
+
const redirectUrl = req.query.redirect || '/';
|
|
70
|
+
// Store state for validation
|
|
71
|
+
stateStore.set(state, {
|
|
72
|
+
createdAt: Date.now(),
|
|
73
|
+
redirectUrl,
|
|
74
|
+
});
|
|
75
|
+
const authUrl = (0, github_oauth_service_1.generateAuthorizationUrl)(process.env.GITHUB_CLIENT_ID, process.env.OAUTH_REDIRECT_URI, state);
|
|
76
|
+
res.json({
|
|
77
|
+
success: true,
|
|
78
|
+
authUrl,
|
|
79
|
+
message: 'Redirect to this URL to authenticate with GitHub',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logger_1.AppLogger.error(`Login initiation failed: ${error}`);
|
|
84
|
+
res.status(500).json({
|
|
85
|
+
error: 'Login failed',
|
|
86
|
+
message: 'Failed to initiate GitHub OAuth flow',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* OAuth callback handler
|
|
92
|
+
* GET /auth/callback?code=...&state=...
|
|
93
|
+
*/
|
|
94
|
+
router.get('/callback', async (req, res) => {
|
|
95
|
+
try {
|
|
96
|
+
const { code, state, error, error_description } = req.query;
|
|
97
|
+
// Handle OAuth errors
|
|
98
|
+
if (error) {
|
|
99
|
+
logger_1.AppLogger.warn(`OAuth error: ${error} - ${error_description}`);
|
|
100
|
+
res.status(400).json({
|
|
101
|
+
success: false,
|
|
102
|
+
error: error,
|
|
103
|
+
message: error_description,
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Validate code and state
|
|
108
|
+
if (!code || !state) {
|
|
109
|
+
logger_1.AppLogger.warn('OAuth callback missing code or state');
|
|
110
|
+
res.status(400).json({
|
|
111
|
+
success: false,
|
|
112
|
+
error: 'Missing parameters',
|
|
113
|
+
message: 'OAuth code and state are required',
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Validate state for CSRF protection
|
|
118
|
+
if (!validateState(state)) {
|
|
119
|
+
logger_1.AppLogger.warn(`Invalid or expired state in OAuth callback: ${state}`);
|
|
120
|
+
res.status(400).json({
|
|
121
|
+
success: false,
|
|
122
|
+
error: 'Invalid state',
|
|
123
|
+
message: 'CSRF validation failed',
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!process.env.GITHUB_CLIENT_SECRET) {
|
|
128
|
+
logger_1.AppLogger.error('GitHub client secret not configured');
|
|
129
|
+
res.status(500).json({
|
|
130
|
+
error: 'OAuth not configured',
|
|
131
|
+
message: 'GitHub OAuth is not properly configured',
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Exchange code for access token
|
|
136
|
+
logger_1.AppLogger.debug('Exchanging OAuth code for access token');
|
|
137
|
+
const tokenResponse = await (0, github_oauth_service_1.exchangeCodeForToken)(code, process.env.GITHUB_CLIENT_ID, process.env.GITHUB_CLIENT_SECRET, process.env.OAUTH_REDIRECT_URI);
|
|
138
|
+
// Get user information
|
|
139
|
+
logger_1.AppLogger.debug('Retrieving authenticated user information');
|
|
140
|
+
const user = await (0, github_oauth_service_1.getAuthenticatedUser)(tokenResponse.access_token);
|
|
141
|
+
// Create session
|
|
142
|
+
const session = {
|
|
143
|
+
accessToken: tokenResponse.access_token,
|
|
144
|
+
expiresIn: 3600, // 1 hour default
|
|
145
|
+
expiresAt: Date.now() + 24 * 60 * 60 * 1000, // 24 hours
|
|
146
|
+
user,
|
|
147
|
+
scopes: tokenResponse.scope.split(','),
|
|
148
|
+
};
|
|
149
|
+
// Store session and get token
|
|
150
|
+
const sessionToken = (0, auth_middleware_1.storeSession)(session);
|
|
151
|
+
// Get redirect URL
|
|
152
|
+
const redirectUrl = getRedirectUrl(state) || '/';
|
|
153
|
+
// Clear state
|
|
154
|
+
clearState(state);
|
|
155
|
+
logger_1.AppLogger.info(`User authenticated: ${user.login}`);
|
|
156
|
+
res.json({
|
|
157
|
+
success: true,
|
|
158
|
+
message: 'Authentication successful',
|
|
159
|
+
sessionToken,
|
|
160
|
+
redirectUrl,
|
|
161
|
+
user: {
|
|
162
|
+
id: user.id,
|
|
163
|
+
login: user.login,
|
|
164
|
+
name: user.name,
|
|
165
|
+
avatar_url: user.avatar_url,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
logger_1.AppLogger.error(`OAuth callback failed: ${error}`);
|
|
171
|
+
res.status(500).json({
|
|
172
|
+
success: false,
|
|
173
|
+
error: 'Authentication failed',
|
|
174
|
+
message: 'Failed to complete GitHub OAuth flow',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
/**
|
|
179
|
+
* Get current user session
|
|
180
|
+
* GET /auth/me
|
|
181
|
+
*/
|
|
182
|
+
router.get('/me', auth_middleware_1.authenticationMiddleware, (req, res) => {
|
|
183
|
+
try {
|
|
184
|
+
const session = (0, auth_middleware_1.getSessionFromRequest)(req);
|
|
185
|
+
if (!session) {
|
|
186
|
+
res.status(401).json({
|
|
187
|
+
error: 'Unauthorized',
|
|
188
|
+
message: 'No active session',
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
res.json({
|
|
193
|
+
success: true,
|
|
194
|
+
user: {
|
|
195
|
+
id: session.user.id,
|
|
196
|
+
login: session.user.login,
|
|
197
|
+
name: session.user.name,
|
|
198
|
+
email: session.user.email,
|
|
199
|
+
avatar_url: session.user.avatar_url,
|
|
200
|
+
public_repos: session.user.public_repos,
|
|
201
|
+
followers: session.user.followers,
|
|
202
|
+
following: session.user.following,
|
|
203
|
+
},
|
|
204
|
+
scopes: session.scopes,
|
|
205
|
+
expiresAt: session.expiresAt,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
logger_1.AppLogger.error(`Failed to get user session: ${error}`);
|
|
210
|
+
res.status(500).json({
|
|
211
|
+
error: 'Internal server error',
|
|
212
|
+
message: 'Failed to retrieve user information',
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
/**
|
|
217
|
+
* Validate session
|
|
218
|
+
* POST /auth/validate
|
|
219
|
+
*/
|
|
220
|
+
router.post('/validate', auth_middleware_1.authenticationMiddleware, async (req, res) => {
|
|
221
|
+
try {
|
|
222
|
+
const session = (0, auth_middleware_1.getSessionFromRequest)(req);
|
|
223
|
+
if (!session) {
|
|
224
|
+
res.status(401).json({
|
|
225
|
+
success: false,
|
|
226
|
+
valid: false,
|
|
227
|
+
message: 'No active session',
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
// Validate token with GitHub
|
|
232
|
+
const isValid = await (0, github_oauth_service_1.validateToken)(session.accessToken);
|
|
233
|
+
if (!isValid) {
|
|
234
|
+
// Token is no longer valid, invalidate session
|
|
235
|
+
const token = req.headers.authorization?.replace('Bearer ', '') ||
|
|
236
|
+
req.cookies?.['auth-token'];
|
|
237
|
+
if (token) {
|
|
238
|
+
(0, auth_middleware_1.invalidateSession)(token);
|
|
239
|
+
}
|
|
240
|
+
res.status(401).json({
|
|
241
|
+
success: false,
|
|
242
|
+
valid: false,
|
|
243
|
+
message: 'Session token is no longer valid',
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
res.json({
|
|
248
|
+
success: true,
|
|
249
|
+
valid: true,
|
|
250
|
+
message: 'Session is valid',
|
|
251
|
+
expiresAt: session.expiresAt,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
logger_1.AppLogger.error(`Session validation failed: ${error}`);
|
|
256
|
+
res.status(500).json({
|
|
257
|
+
success: false,
|
|
258
|
+
valid: false,
|
|
259
|
+
error: 'Validation failed',
|
|
260
|
+
message: 'Failed to validate session',
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
/**
|
|
265
|
+
* Logout
|
|
266
|
+
* POST /auth/logout
|
|
267
|
+
*/
|
|
268
|
+
router.post('/logout', auth_middleware_1.authenticationMiddleware, (req, res) => {
|
|
269
|
+
try {
|
|
270
|
+
const token = req.headers.authorization?.replace('Bearer ', '') ||
|
|
271
|
+
req.cookies?.['auth-token'];
|
|
272
|
+
if (token) {
|
|
273
|
+
(0, auth_middleware_1.invalidateSession)(token);
|
|
274
|
+
}
|
|
275
|
+
res.json({
|
|
276
|
+
success: true,
|
|
277
|
+
message: 'Logout successful',
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
logger_1.AppLogger.error(`Logout failed: ${error}`);
|
|
282
|
+
res.status(500).json({
|
|
283
|
+
success: false,
|
|
284
|
+
error: 'Logout failed',
|
|
285
|
+
message: 'Failed to logout',
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
/**
|
|
290
|
+
* Refresh session (token)
|
|
291
|
+
* POST /auth/refresh
|
|
292
|
+
*/
|
|
293
|
+
router.post('/refresh', auth_middleware_1.authenticationMiddleware, async (req, res) => {
|
|
294
|
+
try {
|
|
295
|
+
const session = (0, auth_middleware_1.getSessionFromRequest)(req);
|
|
296
|
+
if (!session) {
|
|
297
|
+
res.status(401).json({
|
|
298
|
+
success: false,
|
|
299
|
+
error: 'Unauthorized',
|
|
300
|
+
message: 'No active session',
|
|
301
|
+
});
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
// Validate token is still valid
|
|
305
|
+
const isValid = await (0, github_oauth_service_1.validateToken)(session.accessToken);
|
|
306
|
+
if (!isValid) {
|
|
307
|
+
res.status(401).json({
|
|
308
|
+
success: false,
|
|
309
|
+
error: 'Unauthorized',
|
|
310
|
+
message: 'Token is no longer valid with GitHub',
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// Create new session with updated expiry
|
|
315
|
+
const newSession = {
|
|
316
|
+
...session,
|
|
317
|
+
expiresAt: Date.now() + 24 * 60 * 60 * 1000, // Extend 24 hours
|
|
318
|
+
};
|
|
319
|
+
const newToken = (0, auth_middleware_1.storeSession)(newSession);
|
|
320
|
+
// Invalidate old token
|
|
321
|
+
const oldToken = req.headers.authorization?.replace('Bearer ', '') ||
|
|
322
|
+
req.cookies?.['auth-token'];
|
|
323
|
+
if (oldToken) {
|
|
324
|
+
(0, auth_middleware_1.invalidateSession)(oldToken);
|
|
325
|
+
}
|
|
326
|
+
res.json({
|
|
327
|
+
success: true,
|
|
328
|
+
message: 'Session refreshed successfully',
|
|
329
|
+
sessionToken: newToken,
|
|
330
|
+
expiresAt: newSession.expiresAt,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
logger_1.AppLogger.error(`Session refresh failed: ${error}`);
|
|
335
|
+
res.status(500).json({
|
|
336
|
+
success: false,
|
|
337
|
+
error: 'Refresh failed',
|
|
338
|
+
message: 'Failed to refresh session',
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
exports.default = router;
|