chore: Remove OAuth env variables from client (#20660)

Client will get supported OAuth list from the tenant API, instead of
from injected env variables like `APPSMITH_OAUTH2_GOOGLE_CLIENT_ID`.

This is a step towards moving OAuth configuration out of env variables
completely, and into the backend database, so their configuration can be
tenant-wide, instead of instance-wide.
This commit is contained in:
Shrikant Sharat Kandula 2023-04-30 11:52:42 +05:30 committed by GitHub
parent 3c2bd92cc9
commit 7558df366e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 73 additions and 84 deletions

View File

@ -22,8 +22,6 @@ server {
sub_filter __APPSMITH_SENTRY_DSN__ '${APPSMITH_SENTRY_DSN}';
sub_filter __APPSMITH_SMART_LOOK_ID__ '${APPSMITH_SMART_LOOK_ID}';
sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}';
sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}';
sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '${APPSMITH_MARKETPLACE_ENABLED}';
sub_filter __APPSMITH_SEGMENT_KEY__ '${APPSMITH_SEGMENT_KEY}';
sub_filter __APPSMITH_ALGOLIA_API_ID__ '${APPSMITH_ALGOLIA_API_ID}';

View File

@ -32,8 +32,6 @@ server {
sub_filter __APPSMITH_SENTRY_DSN__ '${APPSMITH_SENTRY_DSN}';
sub_filter __APPSMITH_SMART_LOOK_ID__ '${APPSMITH_SMART_LOOK_ID}';
sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}';
sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}';
sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '${APPSMITH_MARKETPLACE_ENABLED}';
sub_filter __APPSMITH_SEGMENT_KEY__ '${APPSMITH_SEGMENT_KEY}';
sub_filter __APPSMITH_ALGOLIA_API_ID__ '${APPSMITH_ALGOLIA_API_ID}';

View File

@ -29,8 +29,6 @@ server {
proxy_pass __APPSMITH_CLIENT_PROXY_PASS__;
sub_filter __APPSMITH_SENTRY_DSN__ '${APPSMITH_SENTRY_DSN}';
sub_filter __APPSMITH_SMART_LOOK_ID__ '${APPSMITH_SMART_LOOK_ID}';
sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}';
sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}';
sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '${APPSMITH_MARKETPLACE_ENABLED}';
sub_filter __APPSMITH_SEGMENT_KEY__ '${APPSMITH_SEGMENT_KEY}';
sub_filter __APPSMITH_ALGOLIA_API_ID__ '${APPSMITH_ALGOLIA_API_ID}';

View File

@ -55,8 +55,6 @@ module.exports = {
smartLook: {
id: parseConfig("__APPSMITH_SMART_LOOK_ID__"),
},
enableGoogleOAuth: parseConfig("__APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__"),
enableGithubOAuth: parseConfig("__APPSMITH_OAUTH2_GITHUB_CLIENT_ID__"),
disableLoginForm: parseConfig("__APPSMITH_FORM_LOGIN_DISABLED__"),
disableSignup: parseConfig("__APPSMITH_SIGNUP_DISABLED__"),
enableRapidAPI: parseConfig("__APPSMITH_MARKETPLACE_ENABLED__"),

View File

@ -147,8 +147,6 @@
smartLook: {
id: parseConfig("__APPSMITH_SMART_LOOK_ID__"),
},
enableGoogleOAuth: parseConfig("__APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__"),
enableGithubOAuth: parseConfig("__APPSMITH_OAUTH2_GITHUB_CLIENT_ID__"),
disableLoginForm: parseConfig("__APPSMITH_FORM_LOGIN_DISABLED__"),
disableSignup: parseConfig("__APPSMITH_SIGNUP_DISABLED__"),
enableRapidAPI: parseConfig("__APPSMITH_MARKETPLACE_ENABLED__"),

View File

@ -13,8 +13,6 @@ export interface INJECTED_CONFIGS {
smartLook: {
id: string;
};
enableGoogleOAuth: boolean;
enableGithubOAuth: boolean;
disableLoginForm: boolean;
disableSignup: boolean;
enableRapidAPI: boolean;
@ -26,7 +24,6 @@ export interface INJECTED_CONFIGS {
licenseKey: string;
};
enableMixpanel: boolean;
google: string;
enableTNCPP: boolean;
cloudHosting: boolean;
algolia: {
@ -70,12 +67,6 @@ export const getConfigsFromEnvVars = (): INJECTED_CONFIGS => {
smartLook: {
id: process.env.REACT_APP_SMART_LOOK_ID || "",
},
enableGoogleOAuth: process.env.REACT_APP_OAUTH2_GOOGLE_CLIENT_ID
? process.env.REACT_APP_OAUTH2_GOOGLE_CLIENT_ID.length > 0
: false,
enableGithubOAuth: process.env.REACT_APP_OAUTH2_GITHUB_CLIENT_ID
? process.env.REACT_APP_OAUTH2_GITHUB_CLIENT_ID.length > 0
: false,
disableLoginForm: process.env.APPSMITH_FORM_LOGIN_DISABLED
? process.env.APPSMITH_FORM_LOGIN_DISABLED.length > 0
: false,
@ -103,7 +94,6 @@ export const getConfigsFromEnvVars = (): INJECTED_CONFIGS => {
| "debug"
| "error"
| undefined) || "error",
google: process.env.REACT_APP_GOOGLE_MAPS_API_KEY || "",
enableTNCPP: process.env.REACT_APP_TNC_PP
? process.env.REACT_APP_TNC_PP.length > 0
: false,
@ -248,16 +238,10 @@ export const getAppsmithConfigs = (): AppsmithUIConfigs => {
},
enableRapidAPI:
ENV_CONFIG.enableRapidAPI || APPSMITH_FEATURE_CONFIGS.enableRapidAPI,
enableGithubOAuth:
ENV_CONFIG.enableGithubOAuth ||
APPSMITH_FEATURE_CONFIGS.enableGithubOAuth,
disableLoginForm:
ENV_CONFIG.disableLoginForm || APPSMITH_FEATURE_CONFIGS.disableLoginForm,
disableSignup:
ENV_CONFIG.disableSignup || APPSMITH_FEATURE_CONFIGS.disableSignup,
enableGoogleOAuth:
ENV_CONFIG.enableGoogleOAuth ||
APPSMITH_FEATURE_CONFIGS.enableGoogleOAuth,
enableMixpanel:
ENV_CONFIG.enableMixpanel || APPSMITH_FEATURE_CONFIGS.enableMixpanel,
cloudHosting:

View File

@ -37,8 +37,6 @@ export interface AppsmithUIConfigs {
};
enableRapidAPI: boolean;
enableGoogleOAuth: boolean;
enableGithubOAuth: boolean;
disableLoginForm: boolean;
disableSignup: boolean;
enableMixpanel: boolean;

View File

@ -23,9 +23,10 @@ import {
JS_ORIGIN_URI_FORM,
REDIRECT_URL_FORM,
} from "@appsmith/constants/forms";
import { useSelector } from "react-redux";
import { getThirdPartyAuths } from "@appsmith/selectors/tenantSelectors";
const { disableLoginForm, enableGithubOAuth, enableGoogleOAuth } =
getAppsmithConfigs();
const { disableLoginForm } = getAppsmithConfigs();
const FormAuth: AdminConfigType = {
type: SettingCategories.FORM_AUTH,
@ -33,7 +34,6 @@ const FormAuth: AdminConfigType = {
title: "Form Login",
subText: "Enable your workspace to sign in with Appsmith Form.",
canSave: true,
isConnected: false,
settings: [
{
id: "APPSMITH_FORM_LOGIN_DISABLED",
@ -67,13 +67,12 @@ const FormAuth: AdminConfigType = {
],
};
const GoogleAuth: AdminConfigType = {
export const GoogleAuth: AdminConfigType = {
type: SettingCategories.GOOGLE_AUTH,
controlType: SettingTypes.GROUP,
title: "Google Authentication",
subText: "Enable your workspace to sign in with Google (OAuth).",
canSave: true,
isConnected: enableGoogleOAuth,
settings: [
{
id: "APPSMITH_OAUTH2_GOOGLE_READ_MORE",
@ -139,14 +138,13 @@ const GoogleAuth: AdminConfigType = {
],
};
const GithubAuth: AdminConfigType = {
export const GithubAuth: AdminConfigType = {
type: SettingCategories.GITHUB_AUTH,
controlType: SettingTypes.GROUP,
title: "Github Authentication",
subText:
"Enable your workspace to sign in with Github SAML single sign-on (SSO).",
canSave: true,
isConnected: enableGithubOAuth,
settings: [
{
id: "APPSMITH_OAUTH2_GITHUB_READ_MORE",
@ -195,7 +193,6 @@ export const GoogleAuthCallout: AuthMethodType = {
"Enable your workspace to sign in with Google (OAuth 2.0) single sign-on (SSO).",
image: Google,
type: "LINK",
isConnected: enableGoogleOAuth,
};
export const GithubAuthCallout: AuthMethodType = {
@ -206,7 +203,6 @@ export const GithubAuthCallout: AuthMethodType = {
"Enable your workspace to sign in with Github (OAuth 2.0) single sign-on (SSO).",
image: Github,
type: "LINK",
isConnected: enableGithubOAuth,
};
export const SamlAuthCallout: AuthMethodType = {
@ -238,6 +234,11 @@ const AuthMethods = [
];
function AuthMain() {
const socialLoginList = useSelector(getThirdPartyAuths);
GoogleAuth.isConnected = GoogleAuthCallout.isConnected =
socialLoginList.includes("google");
GithubAuth.isConnected = GithubAuthCallout.isConnected =
socialLoginList.includes("github");
return <AuthPage authMethods={AuthMethods} />;
}

View File

@ -47,6 +47,7 @@ import PerformanceTracker, {
import { getIsSafeRedirectURL } from "utils/helpers";
import { getCurrentUser } from "selectors/usersSelectors";
import Container from "pages/UserAuth/Container";
import { getThirdPartyAuths } from "@appsmith/selectors/tenantSelectors";
const { disableLoginForm } = getAppsmithConfigs();
const validate = (values: LoginFormValues, props: ValidateProps) => {
@ -85,7 +86,10 @@ export function Login(props: LoginFormProps) {
const { emailValue: email, error, valid } = props;
const isFormValid = valid && email && !isEmptyString(email);
const location = useLocation();
const socialLoginList = ThirdPartyLoginRegistry.get();
const socialLoginList = [
...useSelector(getThirdPartyAuths),
...ThirdPartyLoginRegistry.get(),
];
const queryParams = new URLSearchParams(location.search);
const invalidCredsForgotPasswordLinkText = createMessage(
LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK,

View File

@ -32,7 +32,7 @@ import type { SignupFormValues } from "pages/UserAuth/helpers";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { SIGNUP_SUBMIT_PATH } from "@appsmith/constants/ApiConstants";
import { connect } from "react-redux";
import { connect, useSelector } from "react-redux";
import type { AppState } from "@appsmith/reducers";
import PerformanceTracker, {
PerformanceTransactionName,
@ -44,6 +44,7 @@ import { useScript, ScriptStatus, AddScriptTo } from "utils/hooks/useScript";
import { getIsSafeRedirectURL } from "utils/helpers";
import Container from "pages/UserAuth/Container";
import { getThirdPartyAuths } from "@appsmith/selectors/tenantSelectors";
declare global {
interface Window {
@ -86,7 +87,10 @@ export function SignUp(props: SignUpFormProps) {
}, []);
const { emailValue: email, error, pristine, submitting, valid } = props;
const isFormValid = valid && email && !isEmptyString(email);
const socialLoginList = ThirdPartyLoginRegistry.get();
const socialLoginList = [
...useSelector(getThirdPartyAuths),
...ThirdPartyLoginRegistry.get(),
];
const shouldDisableSignupButton = pristine || !isFormValid;
const location = useLocation();

View File

@ -45,11 +45,6 @@ const ButtonLogo = styled.img`
width: 24px;
`;
export const SocialLoginTypes = {
GOOGLE: "google",
GITHUB: "github",
};
type SignInType = "SIGNIN" | "SIGNUP";
function SocialLoginButton(props: {
@ -93,7 +88,7 @@ function SocialLoginButton(props: {
);
}
export function ThirdPartyAuth(props: {
function ThirdPartyAuth(props: {
logins: SocialLoginType[];
type: SignInType;
}) {

View File

@ -34,5 +34,8 @@ export const isTenantLoading = (state: AppState) => {
export const getGoogleMapsApiKey = (state: AppState): string | undefined =>
state.tenant?.tenantConfiguration?.googleMapsKey as string | undefined;
export const getThirdPartyAuths = (state: AppState): string[] =>
state.tenant?.tenantConfiguration?.thirdPartyAuths ?? [];
export const getInstanceId = (state: AppState): string =>
state.tenant?.instanceId;

View File

@ -1,31 +1,27 @@
import { getAppsmithConfigs } from "@appsmith/configs";
import { ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH } from "constants/routes";
import type { User } from "constants/userConstants";
const { disableLoginForm, enableGithubOAuth, enableGoogleOAuth } =
getAppsmithConfigs();
export const connectedMethods = [
enableGoogleOAuth,
enableGithubOAuth,
!disableLoginForm,
].filter(Boolean);
const { disableLoginForm } = getAppsmithConfigs();
/* settings is the updated & unsaved settings on Admin settings page */
export const saveAllowed = (settings: any) => {
if (connectedMethods.length === 1) {
export const saveAllowed = (settings: any, socialLoginList: string[]) => {
const connectedMethodsCount =
socialLoginList.length + (disableLoginForm ? 0 : 1);
if (connectedMethodsCount === 1) {
const checkFormLogin = !(
"APPSMITH_FORM_LOGIN_DISABLED" in settings || disableLoginForm
),
checkGoogleAuth =
settings["APPSMITH_OAUTH2_GOOGLE_CLIENT_ID"] !== "" &&
enableGoogleOAuth,
socialLoginList.includes("google"),
checkGithubAuth =
settings["APPSMITH_OAUTH2_GITHUB_CLIENT_ID"] !== "" &&
enableGithubOAuth;
socialLoginList.includes("github");
return checkFormLogin || checkGoogleAuth || checkGithubAuth;
} else {
return connectedMethods.length >= 2;
return connectedMethodsCount >= 2;
}
};

View File

@ -1,17 +1,6 @@
export * from "ce/pages/UserAuth/ThirdPartyAuth";
import {
default as ThirdPartyAuth,
SocialLoginTypes as CE_SocialLoginTypes,
} from "ce/pages/UserAuth/ThirdPartyAuth";
import { getAppsmithConfigs } from "@appsmith/configs";
import { ThirdPartyLoginRegistry } from "pages/UserAuth/ThirdPartyLoginRegistry";
const { enableGithubOAuth, enableGoogleOAuth } = getAppsmithConfigs();
import { default as ThirdPartyAuth } from "ce/pages/UserAuth/ThirdPartyAuth";
export const SocialLoginTypes = CE_SocialLoginTypes;
if (enableGoogleOAuth)
ThirdPartyLoginRegistry.register(SocialLoginTypes.GOOGLE);
if (enableGithubOAuth)
ThirdPartyLoginRegistry.register(SocialLoginTypes.GITHUB);
export const SocialLoginTypes = {};
export default ThirdPartyAuth;

View File

@ -4,7 +4,7 @@ import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import _ from "lodash";
import ProductUpdatesModal from "pages/Applications/ProductUpdatesModal";
import { connect, useDispatch } from "react-redux";
import { connect, useDispatch, useSelector } from "react-redux";
import type { RouteComponentProps } from "react-router";
import { useParams, withRouter } from "react-router";
import type { AppState } from "@appsmith/reducers";
@ -30,10 +30,7 @@ import {
MANDATORY_FIELDS_ERROR,
} from "@appsmith/constants/messages";
import { Toaster, Variant } from "design-system-old";
import {
connectedMethods,
saveAllowed,
} from "@appsmith/utils/adminSettingsHelpers";
import { saveAllowed } from "@appsmith/utils/adminSettingsHelpers";
import AnalyticsUtil from "utils/AnalyticsUtil";
import {
Wrapper,
@ -45,6 +42,10 @@ import {
MaxWidthWrapper,
} from "./components";
import { BackButton } from "components/utils/helperComponents";
import { getThirdPartyAuths } from "@appsmith/selectors/tenantSelectors";
import { getAppsmithConfigs } from "@appsmith/configs";
const { disableLoginForm } = getAppsmithConfigs();
type FormProps = {
settings: Record<string, string>;
@ -80,10 +81,11 @@ export function SettingsForm(
const pageTitle = getSettingLabel(
details?.title || (subCategory ?? category),
);
const socialLoginList = useSelector(getThirdPartyAuths);
const onSave = () => {
if (checkMandatoryFileds()) {
if (saveAllowed(props.settings)) {
if (saveAllowed(props.settings, socialLoginList)) {
AnalyticsUtil.logEvent("ADMIN_SETTINGS_SAVE", {
method: pageTitle,
});
@ -171,7 +173,9 @@ export function SettingsForm(
const disconnect = (currentSettings: AdminConfig) => {
const updatedSettings: any = {};
if (connectedMethods.length >= 2) {
const connectedMethodsCount =
socialLoginList.length + (disableLoginForm ? 0 : 1);
if (connectedMethodsCount >= 2) {
_.forEach(currentSettings, (setting: Setting) => {
if (
!setting.isHidden &&

View File

@ -272,8 +272,6 @@ $(if [[ $use_https == 1 ]]; then echo "
proxy_pass $frontend;
sub_filter __APPSMITH_SENTRY_DSN__ '${APPSMITH_SENTRY_DSN-}';
sub_filter __APPSMITH_SMART_LOOK_ID__ '${APPSMITH_SMART_LOOK_ID-}';
sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID-}';
sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '${APPSMITH_OAUTH2_GITHUB_CLIENT_ID-}';
sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '${APPSMITH_MARKETPLACE_ENABLED-}';
sub_filter __APPSMITH_SEGMENT_KEY__ '${APPSMITH_SEGMENT_KEY-}';
sub_filter __APPSMITH_ALGOLIA_API_ID__ '${APPSMITH_ALGOLIA_API_ID-}';

View File

@ -1,10 +1,29 @@
package com.appsmith.server.domains.ce;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class TenantConfigurationCE {
private String googleMapsKey;
// We add `JsonInclude` here, so that this field is included in the JSON response, even if it is `null`. Reason is,
// if this field is not present, then the existing value in client's state doesn't get updated. It's just the way
// the splat (`...`) operator works in the client. Evidently, we'll want this for all fields in this class.
// In that sense, this class is special, because tenant configuration is cached in `localStorage`, and so it's state
// is preserved across browser refreshes.
@JsonInclude
private List<String> thirdPartyAuths;
public void addThirdPartyAuth(String auth) {
if (thirdPartyAuths == null) {
thirdPartyAuths = new ArrayList<>();
}
thirdPartyAuths.add(auth);
}
}

View File

@ -91,6 +91,14 @@ public class TenantServiceCEImpl extends BaseService<TenantRepository, Tenant, S
config.setGoogleMapsKey(System.getenv("APPSMITH_GOOGLE_MAPS_API_KEY"));
if (StringUtils.hasText(System.getenv("APPSMITH_OAUTH2_GOOGLE_CLIENT_ID"))) {
config.addThirdPartyAuth("google");
}
if (StringUtils.hasText(System.getenv("APPSMITH_OAUTH2_GITHUB_CLIENT_ID"))) {
config.addThirdPartyAuth("github");
}
return tenant;
});
}

View File

@ -38,8 +38,6 @@ $NGINX_SSL_CMNT server_name $custom_domain ;
sub_filter __APPSMITH_SENTRY_DSN__ '\${APPSMITH_SENTRY_DSN}';
sub_filter __APPSMITH_SMART_LOOK_ID__ '\${APPSMITH_SMART_LOOK_ID}';
sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '\${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}';
sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '\${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}';
sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '\${APPSMITH_MARKETPLACE_ENABLED}';
sub_filter __APPSMITH_SEGMENT_KEY__ '\${APPSMITH_SEGMENT_KEY}';
sub_filter __APPSMITH_ALGOLIA_API_ID__ '\${APPSMITH_ALGOLIA_API_ID}';
@ -95,8 +93,6 @@ $NGINX_SSL_CMNT try_files \$uri /index.html =404;
$NGINX_SSL_CMNT
$NGINX_SSL_CMNT sub_filter __APPSMITH_SENTRY_DSN__ '\${APPSMITH_SENTRY_DSN}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_SMART_LOOK_ID__ '\${APPSMITH_SMART_LOOK_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '\${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '\${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '\${APPSMITH_MARKETPLACE_ENABLED}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_SEGMENT_KEY__ '\${APPSMITH_SEGMENT_KEY}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_ALGOLIA_API_ID__ '\${APPSMITH_ALGOLIA_API_ID}';