Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/provider-utils.tsx
2498 views
1
/**
2
* Copyright (c) 2021 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 { AuthProviderType } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
8
import bitbucket from "./images/bitbucket.svg";
9
import github from "./images/github.svg";
10
import gitlab from "./images/gitlab.svg";
11
import azuredevops from "./images/azuredevops.svg";
12
import { gitpodHostUrl } from "./service/service";
13
14
function iconForAuthProvider(type: string | AuthProviderType) {
15
switch (type) {
16
case "GitHub":
17
case AuthProviderType.GITHUB:
18
return <img className="fill-current dark:filter-invert w-5 h-5 ml-3 mr-3 my-auto" src={github} alt="" />;
19
case "GitLab":
20
case AuthProviderType.GITLAB:
21
return <img className="fill-current filter-grayscale w-5 h-5 ml-3 mr-3 my-auto" src={gitlab} alt="" />;
22
case "Bitbucket":
23
case AuthProviderType.BITBUCKET:
24
return <img className="fill-current filter-grayscale w-5 h-5 ml-3 mr-3 my-auto" src={bitbucket} alt="" />;
25
case "BitbucketServer":
26
case AuthProviderType.BITBUCKET_SERVER:
27
return <img className="fill-current filter-grayscale w-5 h-5 ml-3 mr-3 my-auto" src={bitbucket} alt="" />;
28
case "AzureDevOps":
29
case AuthProviderType.AZURE_DEVOPS:
30
return <img className="fill-current filter-grayscale w-5 h-5 ml-3 mr-3 my-auto" src={azuredevops} alt="" />;
31
default:
32
return <></>;
33
}
34
}
35
36
export function toAuthProviderLabel(type: AuthProviderType) {
37
switch (type) {
38
case AuthProviderType.GITHUB:
39
return "GitHub";
40
case AuthProviderType.GITLAB:
41
return "GitLab";
42
case AuthProviderType.BITBUCKET:
43
return "Bitbucket Cloud";
44
case AuthProviderType.BITBUCKET_SERVER:
45
return "Bitbucket Server";
46
case AuthProviderType.AZURE_DEVOPS:
47
return "Azure DevOps";
48
default:
49
return "-";
50
}
51
}
52
53
function simplifyProviderName(host: string) {
54
switch (host) {
55
case "github.com":
56
return "GitHub";
57
case "gitlab.com":
58
return "GitLab";
59
case "bitbucket.org":
60
return "Bitbucket";
61
case "dev.azure.com":
62
return "Azure DevOps";
63
default:
64
return host;
65
}
66
}
67
68
interface WindowMessageHandler {
69
onSuccess?: (payload?: string) => void;
70
onError?: (error: string | { error: string; description?: string }) => void;
71
}
72
73
interface OpenAuthorizeWindowParams extends WindowMessageHandler {
74
login?: boolean;
75
host: string;
76
scopes?: string[];
77
overrideScopes?: boolean;
78
overrideReturn?: string;
79
}
80
81
async function openAuthorizeWindow(params: OpenAuthorizeWindowParams) {
82
const { login, host, scopes, overrideScopes } = params;
83
const successKey = getUniqueSuccessKey();
84
let search = `message=${successKey}`;
85
const returnTo = gitpodHostUrl.with({ pathname: "complete-auth", search: search }).toString();
86
const requestedScopes = scopes || [];
87
const url = login
88
? gitpodHostUrl
89
.withApi({
90
pathname: "/login",
91
search: `host=${host}&returnTo=${encodeURIComponent(returnTo)}`,
92
})
93
.toString()
94
: gitpodHostUrl
95
.withApi({
96
pathname: "/authorize",
97
search: `returnTo=${encodeURIComponent(returnTo)}&host=${host}${
98
overrideScopes ? "&override=true" : ""
99
}&scopes=${requestedScopes.join(",")}`,
100
})
101
.toString();
102
103
openModalWindow(url);
104
105
attachMessageListener(successKey, params);
106
}
107
108
async function redirectToAuthorize(params: OpenAuthorizeWindowParams) {
109
const { login, host, scopes, overrideScopes } = params;
110
const successKey = getUniqueSuccessKey();
111
const searchParamsReturn = new URLSearchParams({ message: successKey });
112
for (const [key, value] of new URLSearchParams(window.location.search)) {
113
if (key === "message") {
114
continue;
115
}
116
searchParamsReturn.append(key, value);
117
}
118
const returnTo = gitpodHostUrl
119
.with({ pathname: window.location.pathname, search: searchParamsReturn.toString(), hash: window.location.hash })
120
.toString();
121
const requestedScopes = scopes ?? [];
122
const url = login
123
? gitpodHostUrl
124
.withApi({
125
pathname: "/login",
126
search: `host=${host}&returnTo=${encodeURIComponent(returnTo)}`,
127
})
128
.toString()
129
: gitpodHostUrl
130
.withApi({
131
pathname: "/authorize",
132
search: `returnTo=${encodeURIComponent(returnTo)}&host=${host}${
133
overrideScopes ? "&override=true" : ""
134
}&scopes=${requestedScopes.join(",")}`,
135
})
136
.toString();
137
138
window.location.href = url;
139
}
140
141
function openModalWindow(url: string) {
142
const width = 800;
143
const height = 800;
144
const left = window.screen.width / 2 - width / 2;
145
const top = window.screen.height / 2 - height / 2;
146
147
// Optimistically assume that the new window was opened.
148
window.open(
149
url,
150
"gitpod-auth-window",
151
`width=${width},height=${height},top=${top},left=${left},status=yes,scrollbars=yes,resizable=yes`,
152
);
153
}
154
155
function parseError(data: string) {
156
let error: string | { error: string; description?: string } = atob(data.substring("error:".length));
157
try {
158
const payload = JSON.parse(error);
159
if (typeof payload === "object" && payload.error) {
160
error = { ...payload };
161
}
162
} catch (error) {
163
console.log(error);
164
}
165
166
return error;
167
}
168
169
function attachMessageListener(successKey: string, { onSuccess, onError }: WindowMessageHandler) {
170
const eventListener = (event: MessageEvent) => {
171
if (event?.origin !== document.location.origin) {
172
return;
173
}
174
175
const killAuthWindow = () => {
176
window.removeEventListener("message", eventListener);
177
178
if (event.source && "close" in event.source && event.source.close) {
179
console.log(`Received Auth Window Result. Closing Window.`);
180
event.source.close();
181
}
182
};
183
184
if (typeof event.data === "string" && event.data.startsWith(successKey)) {
185
killAuthWindow();
186
onSuccess && onSuccess(event.data);
187
}
188
if (typeof event.data === "string" && event.data.startsWith("error:")) {
189
const error = parseError(event.data);
190
191
killAuthWindow();
192
onError && onError(error);
193
}
194
};
195
window.addEventListener("message", eventListener);
196
}
197
198
interface OpenOIDCStartWindowParams extends WindowMessageHandler {
199
orgSlug?: string;
200
configId?: string;
201
activate?: boolean;
202
verify?: boolean;
203
}
204
205
/**
206
* @param orgSlug when empty, tries to log in the user using the SSO for a single-org setup
207
*/
208
async function redirectToOIDC({ orgSlug = "", configId, activate = false, verify = false }: OpenOIDCStartWindowParams) {
209
const successKey = getUniqueSuccessKey();
210
const searchParamsReturn = new URLSearchParams({ message: successKey });
211
for (const [key, value] of new URLSearchParams(window.location.search)) {
212
if (key === "message") {
213
continue;
214
}
215
searchParamsReturn.append(key, value);
216
}
217
const returnTo = gitpodHostUrl
218
.with({ pathname: window.location.pathname, search: searchParamsReturn.toString(), hash: window.location.hash })
219
.toString();
220
const searchParams = new URLSearchParams({ returnTo });
221
if (orgSlug) {
222
searchParams.append("orgSlug", orgSlug);
223
}
224
if (configId) {
225
searchParams.append("id", configId);
226
}
227
if (activate) {
228
searchParams.append("activate", "true");
229
} else if (verify) {
230
searchParams.append("verify", "true");
231
}
232
233
const url = gitpodHostUrl
234
.with(() => ({
235
pathname: `/iam/oidc/start`,
236
search: searchParams.toString(),
237
}))
238
.toString();
239
240
window.location.href = url;
241
}
242
243
async function openOIDCStartWindow(params: OpenOIDCStartWindowParams) {
244
const { orgSlug, configId, activate = false, verify = false } = params;
245
const successKey = getUniqueSuccessKey();
246
let search = `message=${successKey}`;
247
const returnTo = gitpodHostUrl.with({ pathname: "complete-auth", search }).toString();
248
const searchParams = new URLSearchParams({ returnTo });
249
if (orgSlug) {
250
searchParams.append("orgSlug", orgSlug);
251
}
252
if (configId) {
253
searchParams.append("id", configId);
254
}
255
if (activate) {
256
searchParams.append("activate", "true");
257
} else if (verify) {
258
searchParams.append("verify", "true");
259
}
260
261
const url = gitpodHostUrl
262
.with((url) => ({
263
pathname: `/iam/oidc/start`,
264
search: searchParams.toString(),
265
}))
266
.toString();
267
268
openModalWindow(url);
269
270
attachMessageListener(successKey, params);
271
}
272
273
// Used to ensure each callback is handled uniquely
274
let counter = 0;
275
const getUniqueSuccessKey = () => {
276
return `success:${counter++}`;
277
};
278
279
export {
280
iconForAuthProvider,
281
simplifyProviderName,
282
openAuthorizeWindow,
283
openOIDCStartWindow,
284
redirectToAuthorize,
285
redirectToOIDC,
286
parseError,
287
};
288
289