Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/login/SSOLoginForm.tsx
3606 views
1
/**
2
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
3
* Licensed under the GNU Affero General Public License (AGPL).
4
* See License.AGPL.txt in the project root for license information.
5
*/
6
7
import { FC, useCallback, useState } from "react";
8
import Alert from "../components/Alert";
9
import { Button } from "@podkit/buttons/Button";
10
import { TextInputField } from "../components/forms/TextInputField";
11
import { useOnBlurError } from "../hooks/use-onblur-error";
12
import { openOIDCStartWindow } from "../provider-utils";
13
import { useFeatureFlag } from "../data/featureflag-query";
14
import { useLocation } from "react-router";
15
import { useOnboardingState } from "../dedicated-setup/use-needs-setup";
16
import { getOrgSlugFromQuery } from "../data/organizations/orgs-query";
17
import { storageAvailable } from "../utils";
18
19
function getOrgSlugFromPath(path: string) {
20
// '/login/acme' => ['', 'login', 'acme']
21
const pathSegments = path.split("/");
22
if (pathSegments[1] !== "login") {
23
return;
24
}
25
return pathSegments[2];
26
}
27
28
type Props = {
29
onSuccess: () => void;
30
};
31
export const SSOLoginForm: FC<Props> = ({ onSuccess }) => {
32
const location = useLocation();
33
const { data: onboardingState } = useOnboardingState();
34
const singleOrgMode = (onboardingState?.organizationCountTotal || 0) < 2;
35
36
const [orgSlug, setOrgSlug] = useState(
37
getOrgSlugFromPath(location.pathname) || getOrgSlugFromQuery(location.search) || readSSOOrgSlug() || "",
38
);
39
const [error, setError] = useState("");
40
41
const oidcServiceEnabled = useFeatureFlag("oidcServiceEnabled");
42
43
const openLoginWithSSO = useCallback(
44
async (e: React.FormEvent<HTMLFormElement>) => {
45
e.preventDefault();
46
persistSSOOrgSlug(orgSlug.trim());
47
48
try {
49
await openOIDCStartWindow({
50
orgSlug,
51
onSuccess: onSuccess,
52
onError: (payload) => {
53
let errorMessage: string;
54
if (typeof payload === "string") {
55
errorMessage = payload;
56
} else {
57
errorMessage = payload.description ? payload.description : `Error: ${payload.error}`;
58
}
59
setError(errorMessage);
60
},
61
});
62
} catch (error) {
63
console.log(error);
64
}
65
},
66
[onSuccess, orgSlug],
67
);
68
69
const slugError = useOnBlurError(
70
"Organization slug must not be longer than 63 characters.",
71
orgSlug.trim().length <= 63,
72
);
73
74
// Don't render anything if not enabled
75
if (!oidcServiceEnabled) {
76
return null;
77
}
78
79
return (
80
<form onSubmit={openLoginWithSSO}>
81
<div className="mt-10 space-y-2 w-56">
82
{!singleOrgMode && (
83
<TextInputField
84
label="Organization"
85
placeholder="my-organization"
86
value={orgSlug}
87
onChange={setOrgSlug}
88
error={slugError.message}
89
onBlur={slugError.onBlur}
90
/>
91
)}
92
<Button
93
type="submit"
94
className="w-full"
95
variant="secondary"
96
disabled={!singleOrgMode && (!orgSlug.trim() || !slugError.isValid)}
97
>
98
Continue {singleOrgMode ? "" : "with SSO"}
99
</Button>
100
{error && <Alert type="info">{error}</Alert>}
101
</div>
102
</form>
103
);
104
};
105
106
function readSSOOrgSlug(): string | undefined {
107
const isLocalStorageAvailable = storageAvailable("localStorage");
108
if (isLocalStorageAvailable) {
109
return window.localStorage.getItem("sso-org-slug") || undefined;
110
}
111
return undefined;
112
}
113
114
function persistSSOOrgSlug(slug: string) {
115
const isLocalStorageAvailable = storageAvailable("localStorage");
116
if (isLocalStorageAvailable) {
117
window.localStorage.setItem("sso-org-slug", slug.trim());
118
}
119
}
120
121