Skip to content

Commit 2055c8a

Browse files
Flag to disable login UI and endpoints for credentialed auth (#3984)
* Flag to disable login UI and endpoints for credentialed auth * dev build * fix translation key
1 parent feadf1f commit 2055c8a

14 files changed

Lines changed: 177 additions & 44 deletions

File tree

.github/workflows/dev-build.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ concurrency:
66

77
on:
88
push:
9-
branches: ['2095-model-swap-in-chat'] # put your current branch to create a build. Core team only.
9+
branches: ['3982-disable-login-simple-sso'] # put your current branch to create a build. Core team only.
1010
paths-ignore:
1111
- '**.md'
1212
- 'cloud-deployments/*'

docker/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ GID='1000'
329329
# Enable simple SSO passthrough to pre-authenticate users from a third party service.
330330
# See https://docs.anythingllm.com/configuration#simple-sso-passthrough for more information.
331331
# SIMPLE_SSO_ENABLED=1
332+
# SIMPLE_SSO_NO_LOGIN=1
332333

333334
# Allow scraping of any IP address in collector - must be string "true" to be enabled
334335
# See https://docs.anythingllm.com/configuration#local-ip-address-scraping for more information.

frontend/src/hooks/useSimpleSSO.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { useEffect, useState } from "react";
2+
import System from "@/models/system";
3+
4+
/**
5+
* Checks if Simple SSO is enabled and if the user should be redirected to the SSO login page.
6+
* @returns {{loading: boolean, ssoConfig: {enabled: boolean, noLogin: boolean}}}
7+
*/
8+
export default function useSimpleSSO() {
9+
const [loading, setLoading] = useState(true);
10+
const [ssoConfig, setSsoConfig] = useState({
11+
enabled: false,
12+
noLogin: false,
13+
});
14+
15+
useEffect(() => {
16+
async function checkSsoConfig() {
17+
try {
18+
const settings = await System.keys();
19+
setSsoConfig({
20+
enabled: settings?.SimpleSSOEnabled,
21+
noLogin: settings?.SimpleSSONoLogin,
22+
});
23+
} catch (e) {
24+
console.error(e);
25+
} finally {
26+
setLoading(false);
27+
}
28+
}
29+
checkSsoConfig();
30+
}, []);
31+
32+
return { loading, ssoConfig };
33+
}

frontend/src/pages/Login/SSO/simple.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ export default function SimpleSSOPassthrough() {
3939

4040
if (error)
4141
return (
42-
<div className="w-screen h-screen overflow-hidden bg-sidebar flex items-center justify-center flex-col gap-4">
43-
<p className="text-white font-mono text-lg">{error}</p>
44-
<p className="text-white/80 font-mono text-sm">
42+
<div className="w-screen h-screen overflow-hidden bg-theme-bg-primary flex items-center justify-center flex-col gap-4">
43+
<p className="text-theme-text-primary font-mono text-lg">{error}</p>
44+
<p className="text-theme-text-secondary font-mono text-sm">
4545
Please contact the system administrator about this error.
4646
</p>
4747
</div>

frontend/src/pages/Login/index.jsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,24 @@ import { FullScreenLoader } from "@/components/Preloader";
44
import { Navigate } from "react-router-dom";
55
import paths from "@/utils/paths";
66
import useQuery from "@/hooks/useQuery";
7+
import useSimpleSSO from "@/hooks/useSimpleSSO";
78

9+
/**
10+
* Login page that handles both single and multi-user login.
11+
*
12+
* If Simple SSO is enabled and no login is allowed, the user will be redirected to the SSO login page
13+
* which may not have a token so the login will fail.
14+
*
15+
* @returns {JSX.Element}
16+
*/
817
export default function Login() {
918
const query = useQuery();
19+
const { loading: ssoLoading, ssoConfig } = useSimpleSSO();
1020
const { loading, requiresAuth, mode } = usePasswordModal(!!query.get("nt"));
11-
if (loading) return <FullScreenLoader />;
21+
22+
if (loading || ssoLoading) return <FullScreenLoader />;
23+
if (ssoConfig.enabled && ssoConfig.noLogin)
24+
return <Navigate to={paths.sso.login()} />;
1225
if (requiresAuth === false) return <Navigate to={paths.home()} />;
1326

1427
return <PasswordModal mode={mode} />;

frontend/src/pages/OnboardingFlow/Steps/UserSetup/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ const MyTeam = ({ setMultiUserLoginValid, myTeamSubmitRef, navigate }) => {
284284
htmlFor="name"
285285
className="block mb-3 text-sm font-medium text-white"
286286
>
287-
{t("common.adminUsername")}
287+
{t("onboarding.userSetup.adminUsername")}
288288
</label>
289289
<input
290290
name="username"

frontend/src/utils/paths.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ export default {
1818
login: (noTry = false) => {
1919
return `/login${noTry ? "?nt=1" : ""}`;
2020
},
21+
sso: {
22+
login: () => {
23+
return "/sso/simple";
24+
},
25+
},
2126
onboarding: {
2227
home: () => {
2328
return "/onboarding";

server/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ TTS_PROVIDER="native"
326326
# Enable simple SSO passthrough to pre-authenticate users from a third party service.
327327
# See https://docs.anythingllm.com/configuration#simple-sso-passthrough for more information.
328328
# SIMPLE_SSO_ENABLED=1
329+
# SIMPLE_SSO_NO_LOGIN=1
329330

330331
# Allow scraping of any IP address in collector - must be string "true" to be enabled
331332
# See https://docs.anythingllm.com/configuration#local-ip-address-scraping for more information.

server/endpoints/admin.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ const {
2525
} = require("../utils/middleware/multiUserProtected");
2626
const { validatedRequest } = require("../utils/middleware/validatedRequest");
2727
const ImportedPlugin = require("../utils/agents/imported");
28+
const {
29+
simpleSSOLoginDisabledMiddleware,
30+
} = require("../utils/middleware/simpleSSOEnabled");
2831

2932
function adminEndpoints(app) {
3033
if (!app) return;
@@ -168,7 +171,11 @@ function adminEndpoints(app) {
168171

169172
app.post(
170173
"/admin/invite/new",
171-
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
174+
[
175+
validatedRequest,
176+
strictMultiUserRoleValid([ROLES.admin, ROLES.manager]),
177+
simpleSSOLoginDisabledMiddleware,
178+
],
172179
async (request, response) => {
173180
try {
174181
const user = await userFromSession(request, response);

server/endpoints/invite.js

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ const { EventLogs } = require("../models/eventLogs");
22
const { Invite } = require("../models/invite");
33
const { User } = require("../models/user");
44
const { reqBody } = require("../utils/http");
5+
const {
6+
simpleSSOLoginDisabledMiddleware,
7+
} = require("../utils/middleware/simpleSSOEnabled");
58

69
function inviteEndpoints(app) {
710
if (!app) return;
@@ -31,46 +34,50 @@ function inviteEndpoints(app) {
3134
}
3235
});
3336

34-
app.post("/invite/:code", async (request, response) => {
35-
try {
36-
const { code } = request.params;
37-
const { username, password } = reqBody(request);
38-
const invite = await Invite.get({ code });
39-
if (!invite || invite.status !== "pending") {
40-
response
41-
.status(200)
42-
.json({ success: false, error: "Invite not found or is invalid." });
43-
return;
44-
}
37+
app.post(
38+
"/invite/:code",
39+
[simpleSSOLoginDisabledMiddleware],
40+
async (request, response) => {
41+
try {
42+
const { code } = request.params;
43+
const { username, password } = reqBody(request);
44+
const invite = await Invite.get({ code });
45+
if (!invite || invite.status !== "pending") {
46+
response
47+
.status(200)
48+
.json({ success: false, error: "Invite not found or is invalid." });
49+
return;
50+
}
4551

46-
const { user, error } = await User.create({
47-
username,
48-
password,
49-
role: "default",
50-
});
51-
if (!user) {
52-
console.error("Accepting invite:", error);
53-
response
54-
.status(200)
55-
.json({ success: false, error: "Could not create user." });
56-
return;
57-
}
52+
const { user, error } = await User.create({
53+
username,
54+
password,
55+
role: "default",
56+
});
57+
if (!user) {
58+
console.error("Accepting invite:", error);
59+
response
60+
.status(200)
61+
.json({ success: false, error: "Could not create user." });
62+
return;
63+
}
5864

59-
await Invite.markClaimed(invite.id, user);
60-
await EventLogs.logEvent(
61-
"invite_accepted",
62-
{
63-
username: user.username,
64-
},
65-
user.id
66-
);
65+
await Invite.markClaimed(invite.id, user);
66+
await EventLogs.logEvent(
67+
"invite_accepted",
68+
{
69+
username: user.username,
70+
},
71+
user.id
72+
);
6773

68-
response.status(200).json({ success: true, error: null });
69-
} catch (e) {
70-
console.error(e);
71-
response.sendStatus(500).end();
74+
response.status(200).json({ success: true, error: null });
75+
} catch (e) {
76+
console.error(e);
77+
response.sendStatus(500).end();
78+
}
7279
}
73-
});
80+
);
7481
}
7582

7683
module.exports = { inviteEndpoints };

0 commit comments

Comments
 (0)