mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-26 20:16:00 +00:00
oauth working, but cycling sometimes
This commit is contained in:
3
packages/api/env/auth/.env
vendored
3
packages/api/env/auth/.env
vendored
@@ -1,4 +1,5 @@
|
|||||||
DEVMODE=1
|
DEVMODE=1
|
||||||
OAUTH=http://auth.metrostav.vychozi.cz/auth/realms/metrostav/protocol/openid-connect
|
OAUTH_AUTH=http://auth.metrostav.vychozi.cz/auth/realms/metrostav/protocol/openid-connect/auth
|
||||||
|
OAUTH_TOKEN=http://auth.metrostav.vychozi.cz/auth/realms/metrostav/protocol/openid-connect/token
|
||||||
OAUTH_CLIENT_ID=dbgate
|
OAUTH_CLIENT_ID=dbgate
|
||||||
OAUTH_CLIENT_SECRET=ffd5634b-b60a-4c3a-bbec-b4144c73ea2a
|
OAUTH_CLIENT_SECRET=ffd5634b-b60a-4c3a-bbec-b4144c73ea2a
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
"is-electron": "^2.2.1",
|
"is-electron": "^2.2.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"json-stable-stringify": "^1.0.1",
|
"json-stable-stringify": "^1.0.1",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
"line-reader": "^0.4.0",
|
"line-reader": "^0.4.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
|
|||||||
@@ -1,4 +1,39 @@
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const getExpressPath = require('../utility/getExpressPath');
|
||||||
|
const uuidv1 = require('uuid/v1');
|
||||||
|
|
||||||
|
const tokenSecret = uuidv1();
|
||||||
|
|
||||||
|
function shouldAuthorizeApi() {
|
||||||
|
return !!process.env.OAUTH_AUTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
function authMiddleware(req, res, next) {
|
||||||
|
const SKIP_AUTH_PATHS = ['/config/get', '/auth/oauth-token', '/stream'];
|
||||||
|
|
||||||
|
if (!shouldAuthorizeApi()) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
if (SKIP_AUTH_PATHS.find(x => req.path == getExpressPath(x))) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
if (!authHeader) {
|
||||||
|
return res.send(401, 'missing authorization header');
|
||||||
|
}
|
||||||
|
const token = authHeader.split(' ')[1];
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(token, tokenSecret);
|
||||||
|
req.user = decoded;
|
||||||
|
return next();
|
||||||
|
} catch (err) {
|
||||||
|
console.log('&&&&&&&&&&&&&&&&&&&&&& IUNVALID TOKEN');
|
||||||
|
console.log(token);
|
||||||
|
console.log(err);
|
||||||
|
return res.sendStatus(401).send('Invalid Token');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
oauthToken_meta: true,
|
oauthToken_meta: true,
|
||||||
@@ -6,12 +41,25 @@ module.exports = {
|
|||||||
const { redirectUri, code } = params;
|
const { redirectUri, code } = params;
|
||||||
|
|
||||||
const resp = await axios.default.post(
|
const resp = await axios.default.post(
|
||||||
`${process.env.OAUTH}/token`,
|
`${process.env.OAUTH_TOKEN}`,
|
||||||
`grant_type=authorization_code&code=${encodeURIComponent(code)}&redirect_uri=${encodeURIComponent(
|
`grant_type=authorization_code&code=${encodeURIComponent(code)}&redirect_uri=${encodeURIComponent(
|
||||||
redirectUri
|
redirectUri
|
||||||
)}&client_id=${process.env.OAUTH_CLIENT_ID}&client_secret=${process.env.OAUTH_CLIENT_SECRET}`
|
)}&client_id=${process.env.OAUTH_CLIENT_ID}&client_secret=${process.env.OAUTH_CLIENT_SECRET}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return resp.data;
|
const { access_token, refresh_token } = resp.data;
|
||||||
|
|
||||||
|
const payload = jwt.decode(access_token);
|
||||||
|
|
||||||
|
if (access_token) {
|
||||||
|
return {
|
||||||
|
accessToken: jwt.sign({ user: 'oauth' }, tokenSecret, { expiresIn: '1m' }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { error: 'Token not found' };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
authMiddleware,
|
||||||
|
shouldAuthorizeApi,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ module.exports = {
|
|||||||
isDocker: platformInfo.isDocker,
|
isDocker: platformInfo.isDocker,
|
||||||
permissions,
|
permissions,
|
||||||
login,
|
login,
|
||||||
oauth: process.env.OAUTH,
|
oauth: process.env.OAUTH_AUTH,
|
||||||
...currentVersion,
|
...currentVersion,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ function start() {
|
|||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
|
||||||
|
if (auth.shouldAuthorizeApi()) {
|
||||||
|
app.use(auth.authMiddleware);
|
||||||
|
}
|
||||||
|
|
||||||
app.get(getExpressPath('/stream'), async function (req, res) {
|
app.get(getExpressPath('/stream'), async function (req, res) {
|
||||||
res.set({
|
res.set({
|
||||||
'Cache-Control': 'no-cache',
|
'Cache-Control': 'no-cache',
|
||||||
|
|||||||
@@ -20,38 +20,11 @@
|
|||||||
import getElectron from './utility/getElectron';
|
import getElectron from './utility/getElectron';
|
||||||
import AppStartInfo from './widgets/AppStartInfo.svelte';
|
import AppStartInfo from './widgets/AppStartInfo.svelte';
|
||||||
import SettingsListener from './utility/SettingsListener.svelte';
|
import SettingsListener from './utility/SettingsListener.svelte';
|
||||||
|
import { handleAuthOnStartup } from './clientAuth';
|
||||||
|
|
||||||
let loadedApi = false;
|
let loadedApi = false;
|
||||||
let loadedPlugins = false;
|
let loadedPlugins = false;
|
||||||
|
|
||||||
async function handleAuth(config) {
|
|
||||||
if (config.oauth) {
|
|
||||||
const params = new URLSearchParams(location.search);
|
|
||||||
const sentCode = params.get('code');
|
|
||||||
const sentState = params.get('state');
|
|
||||||
if (
|
|
||||||
sentCode &&
|
|
||||||
sentState &&
|
|
||||||
sentState.startsWith('dbg-oauth:') &&
|
|
||||||
sentState == sessionStorage.getItem('oauthState')
|
|
||||||
) {
|
|
||||||
const accessToken = await apiCall('auth/oauth-token', {
|
|
||||||
code: sentCode,
|
|
||||||
redirectUri: location.origin,
|
|
||||||
});
|
|
||||||
console.log('TOKEN', accessToken);
|
|
||||||
} else {
|
|
||||||
const state = `dbg-oauth:${Math.random().toString().substr(2)}`;
|
|
||||||
sessionStorage.setItem('oauthState', state);
|
|
||||||
location.replace(
|
|
||||||
`${config.oauth}/auth?client_id=dbgate&response_type=code&redirect_uri=${encodeURIComponent(
|
|
||||||
location.origin
|
|
||||||
)}&state=${encodeURIComponent(state)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadApi() {
|
async function loadApi() {
|
||||||
// if (shouldWaitForElectronInitialize()) {
|
// if (shouldWaitForElectronInitialize()) {
|
||||||
// setTimeout(loadApi, 100);
|
// setTimeout(loadApi, 100);
|
||||||
@@ -61,10 +34,11 @@
|
|||||||
try {
|
try {
|
||||||
// console.log('************** LOADING API');
|
// console.log('************** LOADING API');
|
||||||
|
|
||||||
|
const config = await getConfig();
|
||||||
|
await handleAuthOnStartup(config);
|
||||||
|
|
||||||
const connections = await apiCall('connections/list');
|
const connections = await apiCall('connections/list');
|
||||||
const settings = await getSettings();
|
const settings = await getSettings();
|
||||||
const config = await getConfig();
|
|
||||||
handleAuth(config);
|
|
||||||
const apps = await getUsedApps();
|
const apps = await getUsedApps();
|
||||||
loadedApi = settings && connections && config && apps;
|
loadedApi = settings && connections && config && apps;
|
||||||
|
|
||||||
|
|||||||
46
packages/web/src/clientAuth.ts
Normal file
46
packages/web/src/clientAuth.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { apiCall } from './utility/api';
|
||||||
|
import { getConfig } from './utility/metadataLoaders';
|
||||||
|
|
||||||
|
export async function handleAuthOnStartup(config) {
|
||||||
|
console.log('********************* handleAuthOnStartup');
|
||||||
|
if (config.oauth) {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const sentCode = params.get('code');
|
||||||
|
const sentState = params.get('state');
|
||||||
|
|
||||||
|
if (
|
||||||
|
sentCode &&
|
||||||
|
sentState &&
|
||||||
|
sentState.startsWith('dbg-oauth:') &&
|
||||||
|
sentState == sessionStorage.getItem('oauthState')
|
||||||
|
) {
|
||||||
|
const authResp = await apiCall('auth/oauth-token', {
|
||||||
|
code: sentCode,
|
||||||
|
redirectUri: location.origin,
|
||||||
|
});
|
||||||
|
const { accessToken } = authResp;
|
||||||
|
console.log('Got new access token:', accessToken);
|
||||||
|
localStorage.setItem('accessToken', accessToken);
|
||||||
|
location.replace('/');
|
||||||
|
} else {
|
||||||
|
if (localStorage.getItem('accessToken')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectToLogin(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function redirectToLogin(config = null) {
|
||||||
|
if (!config) config = await getConfig();
|
||||||
|
|
||||||
|
const state = `dbg-oauth:${Math.random().toString().substr(2)}`;
|
||||||
|
sessionStorage.setItem('oauthState', state);
|
||||||
|
console.log('Redirecting to OAUTH provider');
|
||||||
|
location.replace(
|
||||||
|
`${config.oauth}?client_id=dbgate&response_type=code&redirect_uri=${encodeURIComponent(
|
||||||
|
location.origin
|
||||||
|
)}&state=${encodeURIComponent(state)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,22 +4,6 @@ import './utility/changeCurrentDbByTab';
|
|||||||
import './commands/stdCommands';
|
import './commands/stdCommands';
|
||||||
import localStorageGarbageCollector from './utility/localStorageGarbageCollector';
|
import localStorageGarbageCollector from './utility/localStorageGarbageCollector';
|
||||||
|
|
||||||
const params = new URLSearchParams(location.search);
|
|
||||||
console.log('CODE', params.get('code'));
|
|
||||||
// console.log(
|
|
||||||
// `http://auth.metrostav.vychozi.cz/auth/realms/metrostav/protocol/openid-connect/auth?client_id=dbgate&response_type=code&redirect_uri=${encodeURIComponent(
|
|
||||||
// 'http://localhost:5001/oauth-redirect'
|
|
||||||
// )}&state=1234`
|
|
||||||
// );
|
|
||||||
|
|
||||||
console.log(location);
|
|
||||||
|
|
||||||
// location.replace(
|
|
||||||
// `http://auth.metrostav.vychozi.cz/auth/realms/metrostav/protocol/openid-connect/auth?client_id=dbgate&response_type=code&redirect_uri=${encodeURIComponent(
|
|
||||||
// 'http://localhost:5001/'
|
|
||||||
// )}&state=1234`
|
|
||||||
// );
|
|
||||||
|
|
||||||
localStorageGarbageCollector();
|
localStorageGarbageCollector();
|
||||||
|
|
||||||
const app = new App({
|
const app = new App({
|
||||||
|
|||||||
@@ -4,10 +4,16 @@ import { writable } from 'svelte/store';
|
|||||||
import getElectron from './getElectron';
|
import getElectron from './getElectron';
|
||||||
// import socket from './socket';
|
// import socket from './socket';
|
||||||
import { showSnackbarError } from '../utility/snackbar';
|
import { showSnackbarError } from '../utility/snackbar';
|
||||||
|
import { redirectToLogin } from '../clientAuth';
|
||||||
|
|
||||||
let eventSource;
|
let eventSource;
|
||||||
let apiLogging = false;
|
let apiLogging = false;
|
||||||
// let cacheCleanerRegistered;
|
// let cacheCleanerRegistered;
|
||||||
|
// let apiDisabled = false;
|
||||||
|
|
||||||
|
// export function disableApi() {
|
||||||
|
// apiDisabled = true;
|
||||||
|
// }
|
||||||
|
|
||||||
function wantEventSource() {
|
function wantEventSource() {
|
||||||
if (!eventSource) {
|
if (!eventSource) {
|
||||||
@@ -17,9 +23,9 @@ function wantEventSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processApiResponse(route, args, resp) {
|
function processApiResponse(route, args, resp) {
|
||||||
if (apiLogging) {
|
// if (apiLogging) {
|
||||||
console.log('<<< API RESPONSE', route, args, resp);
|
// console.log('<<< API RESPONSE', route, args, resp);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (resp?.apiErrorMessage) {
|
if (resp?.apiErrorMessage) {
|
||||||
showSnackbarError('API error:' + resp?.apiErrorMessage);
|
showSnackbarError('API error:' + resp?.apiErrorMessage);
|
||||||
@@ -35,6 +41,10 @@ export async function apiCall(route: string, args: {} = undefined) {
|
|||||||
if (apiLogging) {
|
if (apiLogging) {
|
||||||
console.log('>>> API CALL', route, args);
|
console.log('>>> API CALL', route, args);
|
||||||
}
|
}
|
||||||
|
if (apiDisabled) {
|
||||||
|
console.log('Error, API disabled!!');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const electron = getElectron();
|
const electron = getElectron();
|
||||||
if (electron) {
|
if (electron) {
|
||||||
@@ -51,6 +61,11 @@ export async function apiCall(route: string, args: {} = undefined) {
|
|||||||
body: JSON.stringify(args),
|
body: JSON.stringify(args),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (resp.status == 401) {
|
||||||
|
// unauthorized
|
||||||
|
redirectToLogin();
|
||||||
|
}
|
||||||
|
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
return processApiResponse(route, args, json);
|
return processApiResponse(route, args, json);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,5 +15,10 @@ export default function resolveApi() {
|
|||||||
export function resolveApiHeaders() {
|
export function resolveApiHeaders() {
|
||||||
const electron = getElectron();
|
const electron = getElectron();
|
||||||
|
|
||||||
return {};
|
const res = {};
|
||||||
|
const accessToken = localStorage.getItem('accessToken');
|
||||||
|
if (accessToken) {
|
||||||
|
res['Authorization'] = `Bearer ${accessToken}`;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user