Browse Source

feat: Disable registration on frontend and bypass login modal for OIDC

Owen Diffey 2 months ago
parent
commit
36e77dc931

+ 2 - 1
backend/logic/users.js

@@ -662,7 +662,8 @@ class _UsersModule extends CoreClass {
 		let { email } = payload;
 		email = email.toLowerCase().trim();
 
-		if (config.get("registrationDisabled") === true) throw new Error("Registration is not allowed at this time.");
+		if (config.get("registrationDisabled") === true || config.get("apis.oidc.enabled") === true)
+			throw new Error("Registration is not allowed at this time.");
 		if (Array.isArray(config.get("experimental.registration_email_whitelist"))) {
 			const experimentalRegistrationEmailWhitelist = config.get("experimental.registration_email_whitelist");
 

+ 3 - 2
backend/logic/ws.js

@@ -575,14 +575,15 @@ class _WSModule extends CoreClass {
 						enabled: config.get("apis.recaptcha.enabled"),
 						key: config.get("apis.recaptcha.key")
 					},
-					githubAuthentication: config.get("apis.github.enabled"),
+					githubAuthentication: config.get("apis.github.enabled") && !config.get("apis.oidc.enabled"),
 					oidcAuthentication: config.get("apis.oidc.enabled"),
 					messages: config.get("messages"),
 					christmas: config.get("christmas"),
 					footerLinks: config.get("footerLinks"),
 					primaryColor: config.get("primaryColor"),
 					shortcutOverrides: config.get("shortcutOverrides"),
-					registrationDisabled: config.get("registrationDisabled"),
+					registrationDisabled:
+						config.get("registrationDisabled") === true || config.get("apis.oidc.enabled") === true,
 					mailEnabled: config.get("mail.enabled"),
 					discogsEnabled: config.get("apis.discogs.enabled"),
 					experimental: {

+ 25 - 3
frontend/src/components/MainHeader.vue

@@ -2,6 +2,7 @@
 import { defineAsyncComponent, ref, onMounted, watch, nextTick } from "vue";
 import Toast from "toasters";
 import { storeToRefs } from "pinia";
+import { useRoute } from "vue-router";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useConfigStore } from "@/stores/config";
 import { useUserAuthStore } from "@/stores/userAuth";
@@ -18,6 +19,8 @@ defineProps({
 	hideLoggedOut: { type: Boolean, default: false }
 });
 
+const route = useRoute();
+
 const userAuthStore = useUserAuthStore();
 
 const localNightmode = ref(false);
@@ -28,8 +31,13 @@ const broadcastChannel = ref();
 const { socket } = useWebsocketsStore();
 
 const configStore = useConfigStore();
-const { cookie, sitename, registrationDisabled, christmas } =
-	storeToRefs(configStore);
+const {
+	cookie,
+	sitename,
+	registrationDisabled,
+	christmas,
+	oidcAuthentication
+} = storeToRefs(configStore);
 
 const { loggedIn, username } = storeToRefs(userAuthStore);
 const { logout, hasPermission } = userAuthStore;
@@ -59,6 +67,10 @@ const onResize = () => {
 	windowWidth.value = window.innerWidth;
 };
 
+const oidcRedirect = () => {
+	localStorage.setItem("oidc_redirect", route.path);
+};
+
 watch(nightmode, value => {
 	localNightmode.value = value;
 });
@@ -157,7 +169,17 @@ onMounted(async () => {
 				<a class="nav-item" @click="logout()">Logout</a>
 			</span>
 			<span v-if="!loggedIn && !hideLoggedOut" class="grouped">
-				<a class="nav-item" @click="openModal('login')">Login</a>
+				<a
+					v-if="oidcAuthentication"
+					class="nav-item"
+					:href="configStore.urls.api + '/auth/oidc/authorize'"
+					@click="oidcRedirect()"
+				>
+					Login
+				</a>
+				<a v-else class="nav-item" @click="openModal('login')">
+					Login
+				</a>
 				<a
 					v-if="!registrationDisabled"
 					class="nav-item"

+ 1 - 19
frontend/src/components/modals/Login.vue

@@ -19,8 +19,7 @@ const password = ref({
 const passwordElement = ref();
 
 const configStore = useConfigStore();
-const { githubAuthentication, oidcAuthentication, registrationDisabled } =
-	storeToRefs(configStore);
+const { githubAuthentication, registrationDisabled } = storeToRefs(configStore);
 const { login } = useUserAuthStore();
 
 const { openModal, closeCurrentModal } = useModalsStore();
@@ -67,9 +66,6 @@ const changeToRegisterModal = () => {
 const githubRedirect = () => {
 	localStorage.setItem("github_redirect", route.path);
 };
-const oidcRedirect = () => {
-	localStorage.setItem("oidc_redirect", route.path);
-};
 </script>
 
 <template>
@@ -168,20 +164,6 @@ const oidcRedirect = () => {
 						</div>
 						&nbsp;&nbsp;Login with GitHub
 					</a>
-					<a
-						v-if="oidcAuthentication"
-						class="button is-oidc"
-						:href="configStore.urls.api + '/auth/oidc/authorize'"
-						@click="oidcRedirect()"
-					>
-						<div class="icon">
-							<img
-								class="invert"
-								src="/assets/social/github.svg"
-							/>
-						</div>
-						&nbsp;&nbsp;Login with OIDC
-					</a>
 				</div>
 
 				<p

+ 2 - 24
frontend/src/components/modals/Register.vue

@@ -41,12 +41,8 @@ const passwordElement = ref();
 const { register } = useUserAuthStore();
 
 const configStore = useConfigStore();
-const {
-	registrationDisabled,
-	recaptcha,
-	githubAuthentication,
-	oidcAuthentication
-} = storeToRefs(configStore);
+const { registrationDisabled, recaptcha, githubAuthentication } =
+	storeToRefs(configStore);
 const { openModal, closeCurrentModal } = useModalsStore();
 
 const submitModal = () => {
@@ -84,10 +80,6 @@ const githubRedirect = () => {
 	localStorage.setItem("github_redirect", route.path);
 };
 
-const oidcRedirect = () => {
-	localStorage.setItem("oidc_redirect", route.path);
-};
-
 watch(
 	() => username.value.value,
 	value => {
@@ -296,20 +288,6 @@ onMounted(async () => {
 						</div>
 						&nbsp;&nbsp;Register with GitHub
 					</a>
-					<a
-						v-if="oidcAuthentication"
-						class="button is-oidc"
-						:href="configStore.urls.api + '/auth/oidc/authorize'"
-						@click="oidcRedirect()"
-					>
-						<div class="icon">
-							<img
-								class="invert"
-								src="/assets/social/github.svg"
-							/>
-						</div>
-						&nbsp;&nbsp;Register with OIDC
-					</a>
 				</div>
 
 				<p class="content-box-optional-helper">

+ 19 - 2
frontend/src/pages/Home.vue

@@ -38,7 +38,8 @@ const userAuthStore = useUserAuthStore();
 const route = useRoute();
 const router = useRouter();
 
-const { sitename, registrationDisabled } = storeToRefs(configStore);
+const { sitename, registrationDisabled, oidcAuthentication } =
+	storeToRefs(configStore);
 const { loggedIn, userId } = storeToRefs(userAuthStore);
 const { hasPermission } = userAuthStore;
 
@@ -152,6 +153,12 @@ const changeFavoriteOrder = ({ moved }) => {
 	);
 };
 
+const oidcRedirect = () => {
+	localStorage.setItem("oidc_redirect", route.path);
+
+	window.location.href = `${configStore.urls.api}/auth/oidc/authorize`;
+};
+
 watch(
 	() => hasPermission("stations.index.other"),
 	value => {
@@ -178,7 +185,9 @@ onMounted(async () => {
 	) {
 		// Makes sure the login/register modal isn't opened whenever the home page gets remounted due to a code change
 		handledLoginRegisterRedirect.value = true;
-		openModal(route.redirectedFrom.name);
+
+		if (oidcAuthentication.value) oidcRedirect();
+		else openModal(route.redirectedFrom.name);
 	}
 
 	socket.onConnect(() => {
@@ -384,7 +393,15 @@ onBeforeUnmount(() => {
 						/>
 						<span v-else class="logo">{{ sitename }}</span>
 						<div v-if="!loggedIn" class="buttons">
+							<a
+								v-if="oidcAuthentication"
+								class="button login"
+								@click="oidcRedirect()"
+							>
+								{{ t("Login") }}
+							</a>
 							<button
+								v-else
 								class="button login"
 								@click="openModal('login')"
 							>

+ 17 - 2
frontend/src/pages/Settings/Tabs/Account.vue

@@ -8,6 +8,7 @@ import { useWebsocketsStore } from "@/stores/websockets";
 import { useUserAuthStore } from "@/stores/userAuth";
 import { useModalsStore } from "@/stores/modals";
 import _validation from "@/validation";
+import { useConfigStore } from "@/stores/config";
 
 const InputHelpBox = defineAsyncComponent(
 	() => import("@/components/InputHelpBox.vue")
@@ -22,6 +23,7 @@ const QuickConfirm = defineAsyncComponent(
 const settingsStore = useSettingsStore();
 const userAuthStore = useUserAuthStore();
 const route = useRoute();
+const configStore = useConfigStore();
 
 const { socket } = useWebsocketsStore();
 
@@ -29,6 +31,7 @@ const saveButton = ref();
 
 const { userId } = storeToRefs(userAuthStore);
 const { originalUser, modifiedUser } = settingsStore;
+const { oidcAuthentication } = storeToRefs(configStore);
 
 const validation = reactive({
 	username: {
@@ -121,6 +124,8 @@ const changeUsername = () => {
 };
 
 const saveChanges = () => {
+	if (oidcAuthentication.value) return;
+
 	const usernameChanged = modifiedUser.username !== originalUser.username;
 	const emailAddressChanged =
 		modifiedUser.email.address !== originalUser.email.address;
@@ -224,13 +229,17 @@ watch(
 				autocomplete="off"
 				@keypress="onInput('username')"
 				@paste="onInput('username')"
+				:disabled="oidcAuthentication"
 			/>
-			<span v-if="modifiedUser.username" class="character-counter"
+			<span
+				v-if="modifiedUser.username && !oidcAuthentication"
+				class="character-counter"
 				>{{ modifiedUser.username.length }}/32</span
 			>
 		</p>
 		<transition name="fadein-helpbox">
 			<input-help-box
+				v-if="!oidcAuthentication"
 				:entered="validation.username.entered"
 				:valid="validation.username.valid"
 				:message="validation.username.message"
@@ -249,17 +258,23 @@ watch(
 				@keypress="onInput('email')"
 				@paste="onInput('email')"
 				autocomplete="off"
+				:disabled="oidcAuthentication"
 			/>
 		</p>
 		<transition name="fadein-helpbox">
 			<input-help-box
+				v-if="!oidcAuthentication"
 				:entered="validation.email.entered"
 				:valid="validation.email.valid"
 				:message="validation.email.message"
 			/>
 		</transition>
 
-		<SaveButton ref="saveButton" @clicked="saveChanges()" />
+		<SaveButton
+			v-if="!oidcAuthentication"
+			ref="saveButton"
+			@clicked="saveChanges()"
+		/>
 
 		<div class="section-margin-bottom" />
 

+ 9 - 1
frontend/src/pages/Settings/Tabs/Profile.vue

@@ -6,6 +6,7 @@ import { useSettingsStore } from "@/stores/settings";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useUserAuthStore } from "@/stores/userAuth";
 import validation from "@/validation";
+import { useConfigStore } from "@/stores/config";
 
 const ProfilePicture = defineAsyncComponent(
 	() => import("@/components/ProfilePicture.vue")
@@ -16,6 +17,7 @@ const SaveButton = defineAsyncComponent(
 
 const settingsStore = useSettingsStore();
 const userAuthStore = useUserAuthStore();
+const configStore = useConfigStore();
 
 const { socket } = useWebsocketsStore();
 
@@ -23,10 +25,13 @@ const saveButton = ref();
 
 const { userId } = storeToRefs(userAuthStore);
 const { originalUser, modifiedUser } = settingsStore;
+const { oidcAuthentication } = storeToRefs(configStore);
 
 const { updateOriginalUser } = settingsStore;
 
 const changeName = () => {
+	if (oidcAuthentication.value) return null;
+
 	modifiedUser.name = modifiedUser.name.replaceAll(/ +/g, " ").trim();
 	const { name } = modifiedUser;
 
@@ -211,8 +216,11 @@ const saveChanges = () => {
 				placeholder="Enter name here..."
 				maxlength="64"
 				v-model="modifiedUser.name"
+				:disabled="oidcAuthentication"
 			/>
-			<span v-if="modifiedUser.name" class="character-counter"
+			<span
+				v-if="modifiedUser.name && !oidcAuthentication"
+				class="character-counter"
 				>{{ modifiedUser.name.length }}/64</span
 			>
 		</p>

+ 9 - 2
frontend/src/pages/Settings/Tabs/Security.vue

@@ -16,7 +16,8 @@ const QuickConfirm = defineAsyncComponent(
 );
 
 const configStore = useConfigStore();
-const { githubAuthentication, sitename } = storeToRefs(configStore);
+const { githubAuthentication, sitename, oidcAuthentication } =
+	storeToRefs(configStore);
 const settingsStore = useSettingsStore();
 const userAuthStore = useUserAuthStore();
 
@@ -57,6 +58,8 @@ const onInput = inputName => {
 	validation[inputName].entered = true;
 };
 const changePassword = () => {
+	if (oidcAuthentication.value) return null;
+
 	const newPassword = validation.newPassword.value;
 
 	if (validation.oldPassword.value === "")
@@ -81,11 +84,15 @@ const changePassword = () => {
 	);
 };
 const unlinkPassword = () => {
+	if (oidcAuthentication.value) return;
+
 	socket.dispatch("users.unlinkPassword", res => {
 		new Toast(res.message);
 	});
 };
 const unlinkGitHub = () => {
+	if (!githubAuthentication.value) return;
+
 	socket.dispatch("users.unlinkGitHub", res => {
 		new Toast(res.message);
 	});
@@ -197,7 +204,7 @@ watch(validation, newValidation => {
 			<div class="section-margin-bottom" />
 		</div>
 
-		<div v-if="!isPasswordLinked">
+		<div v-if="!isPasswordLinked && !oidcAuthentication">
 			<h4 class="section-title">Add a password</h4>
 			<p class="section-description">
 				Add a password, as an alternative to signing in with GitHub