diff --git a/.env b/.env index 96e5897b..85f38dd3 100644 --- a/.env +++ b/.env @@ -39,3 +39,5 @@ # SSO_SERVER_URL='127.0.0.1:10009' # SSO_CERT_PATH='/path/to/certificate/tls.cert' # SSO_MACAROON_PATH='/path/to/folder/containing/macaroons' +# DANGEROUS_NO_SSO_AUTH=false +# LOGOUT_URL='http://thunderhub.io' diff --git a/README.md b/README.md index 46ff3c94..11981d10 100644 --- a/README.md +++ b/README.md @@ -138,10 +138,11 @@ You can define an account to work with SSO cookie authentication by adding the f # ----------- # SSO Account Configs # ----------- -COOKIE_PATH = '/path/to/cookie/file/.cookie'; # i.e. '/data/.cookie' -SSO_SERVER_URL = 'url and port to node'; # i.e. '127.0.0.1:10009' -SSO_CERT_PATH = '/path/to/tls/certificate'; # i.e. '\lnd\alice\tls.cert' -SSO_MACAROON_PATH = '/path/to/macaroon/folder'; # i.e. '\lnd\alice\data\chain\bitcoin\regtest\' +COOKIE_PATH = '/path/to/cookie/file/.cookie' # i.e. '/data/.cookie' +SSO_SERVER_URL = 'url and port to node' # i.e. '127.0.0.1:10009' +SSO_CERT_PATH = '/path/to/tls/certificate' # i.e. '\lnd\alice\tls.cert' +SSO_MACAROON_PATH = '/path/to/macaroon/folder' # i.e. '\lnd\alice\data\chain\bitcoin\regtest\' +LOGOUT_URL = 'http://LogoutToThisUrl.com' # If not set it will logout to "/login" ``` To login to this account you must add the cookie file content to the end of your ThunderHub url. For example: @@ -152,6 +153,30 @@ http://localhost:3000/sso?token=[COOKIE] Replace `[COOKIE]` with the contents of the `.cookie` file. +### SSO Account without authentication + +You can DANGEROUSLY remove SSO authentication. This is useful for example if you plan on running ThunderHub **only** on your local network or through TOR. + +**DO NOT enable this option if your ThunderHub instance is available on the internet or your funds will probably be lost.** + +The configuration for a non authenticated SSO account would look like this: + +```bash +# ----------- +# SSO Account Configs +# ----------- +SSO_SERVER_URL = 'url and port to node'; # i.e. '127.0.0.1:10009' +SSO_CERT_PATH = '/path/to/tls/certificate'; # i.e. '\lnd\alice\tls.cert' +SSO_MACAROON_PATH = '/path/to/macaroon/folder'; # i.e. '\lnd\alice\data\chain\bitcoin\regtest\' +DANGEROUS_NO_SSO_AUTH = 'true' # Default: false +``` + +To login to this account go to the following url: + +``` +http://localhost:3000/sso?token=1 +``` + ### Server Accounts You can add accounts on the server by adding this parameter to the `.env` file: diff --git a/next.config.js b/next.config.js index aca97c8d..d66a8719 100644 --- a/next.config.js +++ b/next.config.js @@ -14,6 +14,8 @@ module.exports = withBundleAnalyzer({ lnCertPath: process.env.SSO_CERT_PATH || '', macaroonPath: process.env.SSO_MACAROON_PATH || '', accountConfigPath: process.env.ACCOUNT_CONFIG_PATH || '', + dangerousNoSSOAuth: + process.env.DANGEROUS_NO_SSO_AUTH === 'true' ? true : false, }, publicRuntimeConfig: { nodeEnv: process.env.NODE_ENV || 'development', @@ -28,5 +30,6 @@ module.exports = withBundleAnalyzer({ disableLinks: process.env.DISABLE_LINKS === 'true' ? true : false, disableLnMarkets: process.env.DISABLE_LNMARKETS === 'true' ? true : false, noVersionCheck: process.env.NO_VERSION_CHECK === 'true' ? true : false, + logoutUrl: process.env.LOGOUT_URL || '', }, }); diff --git a/server/schema/auth/resolvers.ts b/server/schema/auth/resolvers.ts index e0df9cc7..bd0cc263 100644 --- a/server/schema/auth/resolvers.ts +++ b/server/schema/auth/resolvers.ts @@ -12,7 +12,7 @@ import { toWithError } from 'server/helpers/async'; import { decodeMacaroon, isCorrectPassword } from 'server/helpers/crypto'; const { serverRuntimeConfig } = getConfig() || {}; -const { cookiePath, nodeEnv } = serverRuntimeConfig || {}; +const { cookiePath, nodeEnv, dangerousNoSSOAuth } = serverRuntimeConfig || {}; export const authResolvers = { Mutation: { @@ -33,22 +33,32 @@ export const authResolvers = { return false; } - if (!cookie) { - return false; - } + if (dangerousNoSSOAuth) { + logger.warn( + 'SSO authentication is disabled. Make sure this is what you want.' + ); + } else { + // No cookie or cookiePath needed when SSO authentication is turned off + if (!cookie) { + return false; + } - if (cookiePath === '') { - logger.warn('SSO auth not available since no cookie path was provided'); - return false; + if (cookiePath === '') { + logger.warn( + 'SSO auth not available since no cookie path was provided' + ); + return false; + } } const cookieFile = readCookie(cookiePath); if ( (cookieFile && cookieFile.trim() === cookie.trim()) || - nodeEnv === 'development' + nodeEnv === 'development' || + dangerousNoSSOAuth ) { - refreshCookie(cookiePath); + cookiePath && refreshCookie(cookiePath); const { lnd } = authenticatedLndGrpc(sso); const [, error] = await toWithError( diff --git a/src/components/accounts/ServerAccounts.tsx b/src/components/accounts/ServerAccounts.tsx index 12b63dbd..92c427d4 100644 --- a/src/components/accounts/ServerAccounts.tsx +++ b/src/components/accounts/ServerAccounts.tsx @@ -4,8 +4,12 @@ import { appendBasePath } from 'src/utils/basePath'; import { getUrlParam } from 'src/utils/url'; import { toast } from 'react-toastify'; import { getErrorContent } from 'src/utils/error'; +import getConfig from 'next/config'; import { useGetAuthTokenMutation } from 'src/graphql/mutations/__generated__/getAuthToken.generated'; +const { publicRuntimeConfig } = getConfig(); +const { logoutUrl } = publicRuntimeConfig; + export const ServerAccounts: React.FC = () => { const { push, query } = useRouter(); @@ -16,7 +20,7 @@ export const ServerAccounts: React.FC = () => { refetchQueries: ['GetNodeInfo'], onError: error => { toast.error(getErrorContent(error)); - push(appendBasePath('/login')); + push(logoutUrl || appendBasePath('/login')); }, }); @@ -24,7 +28,7 @@ export const ServerAccounts: React.FC = () => { if (cookieParam) { getToken(); } else { - push(appendBasePath('/login')); + push(logoutUrl || appendBasePath('/login')); } }, [cookieParam, push, getToken]); diff --git a/src/components/logoutButton/index.tsx b/src/components/logoutButton/index.tsx index a26cec99..8e62eca3 100644 --- a/src/components/logoutButton/index.tsx +++ b/src/components/logoutButton/index.tsx @@ -7,9 +7,13 @@ import { HeaderNavButton } from 'src/layouts/header/Header.styled'; import styled from 'styled-components'; import { themeColors } from 'src/styles/Themes'; import ScaleLoader from 'react-spinners/ScaleLoader'; +import getConfig from 'next/config'; import { appendBasePath } from '../../utils/basePath'; import { useChatDispatch } from '../../context/ChatContext'; +const { publicRuntimeConfig } = getConfig(); +const { logoutUrl } = publicRuntimeConfig; + const Logout = styled.button` cursor: pointer; text-decoration: none; @@ -33,7 +37,8 @@ export const LogoutButton = () => { if (data && data.logout) { dispatchChat({ type: 'disconnected' }); client.clearStore(); - push(appendBasePath('/login')); + + push(logoutUrl || appendBasePath('/login')); } }, [data, dispatchChat, push, client]); diff --git a/src/components/statusCheck/StatusCheck.tsx b/src/components/statusCheck/StatusCheck.tsx index d60ba2d8..361b5347 100644 --- a/src/components/statusCheck/StatusCheck.tsx +++ b/src/components/statusCheck/StatusCheck.tsx @@ -2,8 +2,12 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; import { toast } from 'react-toastify'; import { useGetNodeInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated'; +import getConfig from 'next/config'; import { appendBasePath } from '../../utils/basePath'; +const { publicRuntimeConfig } = getConfig(); +const { logoutUrl } = publicRuntimeConfig; + export const StatusCheck: React.FC = () => { const { push } = useRouter(); @@ -17,7 +21,7 @@ export const StatusCheck: React.FC = () => { if (error) { toast.error(`Unable to connect to node`); stopPolling(); - push(appendBasePath('/login')); + push(logoutUrl || appendBasePath('/login')); } }, [error, push, stopPolling]); diff --git a/src/utils/ssr.ts b/src/utils/ssr.ts index 7891719d..0c0325d0 100644 --- a/src/utils/ssr.ts +++ b/src/utils/ssr.ts @@ -4,6 +4,10 @@ import { parseCookies } from 'src/utils/cookies'; import { DocumentNode } from 'graphql'; import { appConstants } from 'server/utils/appConstants'; import { GET_AUTH_TOKEN } from 'src/graphql/mutations/getAuthToken'; +import getConfig from 'next/config'; + +const { publicRuntimeConfig } = getConfig(); +const { logoutUrl } = publicRuntimeConfig; const cookieProps = ( context: NextPageContext, @@ -17,7 +21,7 @@ const cookieProps = ( const hasToken = !!cookies[appConstants.tokenCookieName]; if (!cookies[appConstants.cookieName] && !noAuth) { - context.res?.writeHead(302, { Location: '/login' }); + context.res?.writeHead(302, { Location: logoutUrl || '/login' }); context.res?.end(); return { theme: 'dark', authenticated: false, hasToken }; diff --git a/src/views/settings/Danger.tsx b/src/views/settings/Danger.tsx index 1a610e1e..73c34e36 100644 --- a/src/views/settings/Danger.tsx +++ b/src/views/settings/Danger.tsx @@ -3,6 +3,7 @@ import styled from 'styled-components'; import { AlertCircle } from 'react-feather'; import { useRouter } from 'next/router'; import { useLogoutMutation } from 'src/graphql/mutations/__generated__/logout.generated'; +import getConfig from 'next/config'; import { Card, CardWithTitle, @@ -15,6 +16,9 @@ import { ColorButton } from '../../components/buttons/colorButton/ColorButton'; import { appendBasePath } from '../../utils/basePath'; import { useChatDispatch } from '../../context/ChatContext'; +const { publicRuntimeConfig } = getConfig(); +const { logoutUrl } = publicRuntimeConfig; + export const ButtonRow = styled.div` width: auto; display: flex; @@ -56,7 +60,7 @@ export const DangerView = () => { const { push } = useRouter(); const [logout] = useLogoutMutation({ - onCompleted: () => push(appendBasePath('/login')), + onCompleted: () => push(logoutUrl || appendBasePath('/login')), }); const handleDeleteAll = () => {