diff --git a/packages/api/src/auth/authProvider.js b/packages/api/src/auth/authProvider.js index 2b0e69da7..074a351cc 100644 --- a/packages/api/src/auth/authProvider.js +++ b/packages/api/src/auth/authProvider.js @@ -23,10 +23,6 @@ class AuthProviderBase { }; } - shouldAuthorizeApi() { - return false; - } - oauthToken(params) { return {}; } @@ -46,14 +42,6 @@ class AuthProviderBase { return permissions || process.env.PERMISSIONS; } - isLoginForm() { - return false; - } - - getAdditionalConfigProps() { - return {}; - } - getLoginPageConnections() { return null; } @@ -68,15 +56,17 @@ class AuthProviderBase { workflowType: 'anonymous', }; } + + redirect({ state }) { + return { + status: 'error', + }; + } } class OAuthProvider extends AuthProviderBase { amoid = 'oauth'; - shouldAuthorizeApi() { - return true; - } - async oauthToken(params) { const { redirectUri, code } = params; @@ -143,6 +133,18 @@ class OAuthProvider extends AuthProviderBase { workflowType: 'redirect', }; } + + redirect({ state, redirectUri }) { + const scopeParam = process.env.OAUTH_SCOPE ? `&scope=${process.env.OAUTH_SCOPE}` : ''; + return { + status: 'ok', + uri: `${process.env.OAUTH_AUTH}?client_id=${ + process.env.OAUTH_CLIENT_ID + }&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent( + state + )}${scopeParam}`, + }; + } } class ADProvider extends AuthProviderBase { @@ -182,14 +184,6 @@ class ADProvider extends AuthProviderBase { } } - shouldAuthorizeApi() { - return !process.env.BASIC_AUTH; - } - - isLoginForm() { - return !process.env.BASIC_AUTH; - } - toJson() { return { ...super.toJson(), @@ -217,14 +211,6 @@ class LoginsProvider extends AuthProviderBase { return { error: 'Invalid credentials' }; } - shouldAuthorizeApi() { - return !process.env.BASIC_AUTH; - } - - isLoginForm() { - return !process.env.BASIC_AUTH; - } - toJson() { return { ...super.toJson(), @@ -236,10 +222,6 @@ class LoginsProvider extends AuthProviderBase { class DenyAllProvider extends AuthProviderBase { amoid = 'deny'; - shouldAuthorizeApi() { - return true; - } - async login(login, password, options = undefined) { return { error: 'Login not allowed' }; } diff --git a/packages/api/src/controllers/auth.js b/packages/api/src/controllers/auth.js index c61bf96a2..fb4565424 100644 --- a/packages/api/src/controllers/auth.js +++ b/packages/api/src/controllers/auth.js @@ -5,7 +5,12 @@ const { getLogger } = require('dbgate-tools'); const AD = require('activedirectory2').promiseWrapper; const crypto = require('crypto'); const { getTokenSecret, getTokenLifetime } = require('../auth/authCommon'); -const { getAuthProviderFromReq, getAuthProviders, getDefaultAuthProvider, getAuthProviderById } = require('../auth/authProvider'); +const { + getAuthProviderFromReq, + getAuthProviders, + getDefaultAuthProvider, + getAuthProviderById, +} = require('../auth/authProvider'); const storage = require('./storage'); const logger = getLogger('auth'); @@ -27,6 +32,7 @@ function authMiddleware(req, res, next) { '/config/get-settings', '/auth/oauth-token', '/auth/login', + '/auth/redirect', '/stream', 'storage/get-connections-for-login-page', 'auth/get-providers', @@ -37,11 +43,13 @@ function authMiddleware(req, res, next) { // console.log('********************* getAuthProvider()', getAuthProvider()); - const isAdminPage = req.headers['x-is-admin-page'] == 'true'; + // const isAdminPage = req.headers['x-is-admin-page'] == 'true'; - if (!isAdminPage && !getAuthProviderFromReq(req).shouldAuthorizeApi()) { + if (process.env.BASIC_AUTH) { + // API is not authorized for basic auth return next(); } + let skipAuth = !!SKIP_AUTH_PATHS.find(x => req.path == getExpressPath(x)); const authHeader = req.headers.authorization; @@ -70,7 +78,8 @@ function authMiddleware(req, res, next) { module.exports = { oauthToken_meta: true, async oauthToken(params) { - return getDefaultAuthProvider().oauthToken(params); + const { amoid } = params; + return getAuthProviderById(amoid).oauthToken(params); }, login_meta: true, async login(params) { @@ -107,5 +116,11 @@ module.exports = { }; }, + redirect_meta: true, + async redirect(params) { + const { amoid } = params; + return getAuthProviderById(amoid).redirect(params); + }, + authMiddleware, }; diff --git a/packages/api/src/controllers/config.js b/packages/api/src/controllers/config.js index 85c3cdc46..1c70c64a5 100644 --- a/packages/api/src/controllers/config.js +++ b/packages/api/src/controllers/config.js @@ -31,8 +31,6 @@ module.exports = { const authProvider = getAuthProviderFromReq(req); const login = authProvider.getCurrentLogin(req); const permissions = authProvider.getCurrentPermissions(req); - const isLoginForm = authProvider.isLoginForm(); - const additionalConfigProps = authProvider.getAdditionalConfigProps(); const isUserLoggedIn = authProvider.isUserLoggedIn(req); const singleConid = authProvider.getSingleConnectionId(req); @@ -52,12 +50,17 @@ module.exports = { isDocker: platformInfo.isDocker, isElectron: platformInfo.isElectron, isLicenseValid: platformInfo.isLicenseValid, - licenseError: platformInfo.licenseError, + checkedLicense: platformInfo.checkedLicense, permissions, login, - ...additionalConfigProps, - isLoginForm, - isAdminLoginForm: !!(process.env.STORAGE_DATABASE && process.env.ADMIN_PASSWORD && !process.env.BASIC_AUTH), + // ...additionalConfigProps, + isBasicAuth: !!process.env.BASIC_AUTH, + isAdminLoginForm: !!( + process.env.STORAGE_DATABASE && + process.env.ADMIN_PASSWORD && + !process.env.BASIC_AUTH && + platformInfo.checkedLicense?.type == 'premium' + ), storageDatabase: process.env.STORAGE_DATABASE, logsFilePath: getLogsFilePath(), connectionsFilePath: path.join(datadir(), 'connections.jsonl'), diff --git a/packages/api/src/utility/checkLicense.js b/packages/api/src/utility/checkLicense.js index 78e37202c..b43b3c1f7 100644 --- a/packages/api/src/utility/checkLicense.js +++ b/packages/api/src/utility/checkLicense.js @@ -1,5 +1,8 @@ function checkLicense() { - return null; + return { + status: 'ok', + type: 'community', + }; } module.exports = { diff --git a/packages/api/src/utility/platformInfo.js b/packages/api/src/utility/platformInfo.js index f11cbf86d..9464b77ab 100644 --- a/packages/api/src/utility/platformInfo.js +++ b/packages/api/src/utility/platformInfo.js @@ -13,8 +13,7 @@ const isDocker = fs.existsSync('/home/dbgate-docker/public'); const isDevMode = process.env.DEVMODE == '1'; const isNpmDist = !!global['IS_NPM_DIST']; const isForkedApi = processArgs.isForkedApi; -const licenseError = checkLicense(); -const isLicenseValid = licenseError == null; +const checkedLicense = checkLicense(); // function moduleAvailable(name) { // try { @@ -33,8 +32,8 @@ const platformInfo = { isElectronBundle: isElectron() && !isDevMode, isForkedApi, isElectron: isElectron(), - isLicenseValid, - licenseError, + checkedLicense, + isLicenseValid: checkedLicense?.status == 'ok', isDevMode, isNpmDist, isSnap: process.env.ELECTRON_SNAP == 'true', diff --git a/packages/web/src/ErrorPage.svelte b/packages/web/src/ErrorPage.svelte index e04f5b034..e053195da 100644 --- a/packages/web/src/ErrorPage.svelte +++ b/packages/web/src/ErrorPage.svelte @@ -24,9 +24,9 @@
Configuration error
- {#if $config?.isLicenseValid == false} + {#if $config?.checkedLicense?.status == 'error'} {:else if error} diff --git a/packages/web/src/LoginPage.svelte b/packages/web/src/LoginPage.svelte index 79d6c071d..e01ee7a3d 100644 --- a/packages/web/src/LoginPage.svelte +++ b/packages/web/src/LoginPage.svelte @@ -209,6 +209,22 @@ return; } internalRedirectTo(`/?page=not-logged`); + } else if (workflowType == 'redirect') { + const state = `dbg-oauth:${strmid}:${$values.amoid}`; + + sessionStorage.setItem('oauthState', state); + console.log('Redirecting to OAUTH provider'); + + const resp = await apiCall('auth/redirect', { + amoid: $values.amoid, + state, + redirectUri: location.origin + location.pathname, + }); + + const { uri } = resp; + if (uri) { + location.replace(uri); + } } }} /> diff --git a/packages/web/src/clientAuth.ts b/packages/web/src/clientAuth.ts index 4e9b6c60b..582f597da 100644 --- a/packages/web/src/clientAuth.ts +++ b/packages/web/src/clientAuth.ts @@ -1,4 +1,4 @@ -import { apiCall, enableApi } from './utility/api'; +import { apiCall, enableApi, getAuthCategory } from './utility/api'; import { getConfig } from './utility/metadataLoaders'; import { isAdminPage } from './utility/pageDefs'; @@ -40,9 +40,12 @@ export function handleOauthCallback() { const sentCode = params.get('code'); if (isOauthCallback()) { + const [_prefix, strmid, amoid] = sessionStorage.getItem('oauthState').split(':'); + sessionStorage.removeItem('oauthState'); apiCall('auth/oauth-token', { code: sentCode, + amoid, redirectUri: location.origin + location.pathname, }).then(authResp => { const { accessToken, error, errorMessage } = authResp; @@ -113,7 +116,7 @@ export async function handleAuthOnStartup(config, isAdminPage = false) { return; } - if (config.isAdminLoginForm && isAdminPage) { + if (getAuthCategory(config) == 'admin') { if (localStorage.getItem('adminAccessToken')) { return; } @@ -122,10 +125,10 @@ export async function handleAuthOnStartup(config, isAdminPage = false) { return; } - if (config.oauth) { - console.log('OAUTH callback URL:', location.origin + location.pathname); - } - if (config.oauth || config.isLoginForm) { + // if (config.oauth) { + // console.log('OAUTH callback URL:', location.origin + location.pathname); + // } + if (getAuthCategory(config) == 'token') { if (localStorage.getItem('accessToken')) { return; } @@ -145,7 +148,7 @@ export async function redirectToLogin(config = null, force = false) { config = await getConfig(); } - if (config.isLoginForm) { + if (getAuthCategory(config) == 'token') { if (!force) { const params = new URLSearchParams(location.search); if (params.get('page') == 'login' || params.get('page') == 'admin-login' || params.get('page') == 'not-logged') { diff --git a/packages/web/src/utility/api.ts b/packages/web/src/utility/api.ts index 875c44c4c..b8c4eea47 100644 --- a/packages/web/src/utility/api.ts +++ b/packages/web/src/utility/api.ts @@ -12,6 +12,7 @@ import uuidv1 from 'uuid/v1'; import { openWebLink } from './exportFileTools'; import { callServerPing } from './connectionsPinger'; import { batchDispatchCacheTriggers, dispatchCacheChange } from './cache'; +import { isAdminPage } from './pageDefs'; export const strmid = uuidv1(); @@ -251,6 +252,19 @@ export function installNewVolatileConnectionListener() { }); } +export function getAuthCategory(config) { + if (config.isBasicAuth) { + return 'basic'; + } + if (isAdminPage() && config.isAdminLoginForm) { + return 'admin'; + } + if (getElectron()) { + return 'electron'; + } + return 'token'; +} + function enableApiLog() { apiLogging = true; console.log('API loggin enabled'); diff --git a/packages/web/src/utility/resolveApi.ts b/packages/web/src/utility/resolveApi.ts index 40329867b..091806077 100644 --- a/packages/web/src/utility/resolveApi.ts +++ b/packages/web/src/utility/resolveApi.ts @@ -21,8 +21,8 @@ export function resolveApiHeaders() { if (accessToken) { res['Authorization'] = `Bearer ${accessToken}`; } - if (isAdminPage()) { - res['x-is-admin-page'] = 'true'; - } + // if (isAdminPage()) { + // res['x-is-admin-page'] = 'true'; + // } return res; }