Skip to content

Commit e418892

Browse files
committed
Use BroadcastChannel to automatically authenticate with Git
1 parent 85db3c2 commit e418892

File tree

8 files changed

+390
-290
lines changed

8 files changed

+390
-290
lines changed

site/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,6 @@
136136
"chrome 66",
137137
"firefox 63",
138138
"edge 79",
139-
"safari 13.1"
139+
"safari 15.4"
140140
]
141141
}

site/src/components/GitAuth/GitAuth.stories.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,43 +15,43 @@ GithubNotAuthenticated.args = {
1515
}
1616

1717
export const GithubAuthenticated = Template.bind({})
18-
GithubNotAuthenticated.args = {
18+
GithubAuthenticated.args = {
1919
type: "github",
2020
authenticated: true,
2121
}
2222

2323
export const GitlabNotAuthenticated = Template.bind({})
24-
GithubNotAuthenticated.args = {
24+
GitlabNotAuthenticated.args = {
2525
type: "gitlab",
2626
authenticated: false,
2727
}
2828

2929
export const GitlabAuthenticated = Template.bind({})
30-
GithubNotAuthenticated.args = {
30+
GitlabAuthenticated.args = {
3131
type: "gitlab",
3232
authenticated: true,
3333
}
3434

3535
export const AzureDevOpsNotAuthenticated = Template.bind({})
36-
GithubNotAuthenticated.args = {
36+
AzureDevOpsNotAuthenticated.args = {
3737
type: "azure-devops",
3838
authenticated: false,
3939
}
4040

4141
export const AzureDevOpsAuthenticated = Template.bind({})
42-
GithubNotAuthenticated.args = {
42+
AzureDevOpsAuthenticated.args = {
4343
type: "azure-devops",
4444
authenticated: true,
4545
}
4646

4747
export const BitbucketNotAuthenticated = Template.bind({})
48-
GithubNotAuthenticated.args = {
48+
BitbucketNotAuthenticated.args = {
4949
type: "bitbucket",
5050
authenticated: false,
5151
}
5252

5353
export const BitbucketAuthenticated = Template.bind({})
54-
GithubNotAuthenticated.args = {
54+
BitbucketAuthenticated.args = {
5555
type: "bitbucket",
5656
authenticated: true,
5757
}
Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
import { FC } from "react"
2-
import * as TypesGen from "api/typesGenerated"
1+
import Button from "@material-ui/core/Button"
32
import { makeStyles } from "@material-ui/core/styles"
4-
import { useLocation } from "react-router-dom"
3+
import { SvgIconProps } from "@material-ui/core/SvgIcon"
4+
import Tooltip from "@material-ui/core/Tooltip"
55
import GitHub from "@material-ui/icons/GitHub"
6-
import { GitlabIcon } from "components/Icons/GitlabIcon"
6+
import * as TypesGen from "api/typesGenerated"
77
import { AzureDevOpsIcon } from "components/Icons/AzureDevOpsIcon"
88
import { BitbucketIcon } from "components/Icons/BitbucketIcon"
9+
import { GitlabIcon } from "components/Icons/GitlabIcon"
910
import { Typography } from "components/Typography/Typography"
10-
import Link from "@material-ui/core/Link"
11-
import Button from "@material-ui/core/Button"
12-
import { Tooltip } from "@material-ui/core"
11+
import { FC } from "react"
1312

1413
export interface GitAuthProps {
1514
type: TypesGen.GitProvider
@@ -25,43 +24,70 @@ export const GitAuth: FC<GitAuthProps> = ({
2524
const styles = useStyles()
2625

2726
let prettyName: string
28-
let icon: JSX.Element
27+
let Icon: (props: SvgIconProps) => JSX.Element
2928
switch (type) {
3029
case "azure-devops":
3130
prettyName = "Azure DevOps"
32-
icon = <AzureDevOpsIcon />
31+
Icon = AzureDevOpsIcon
3332
break
3433
case "bitbucket":
3534
prettyName = "Bitbucket"
36-
icon = <BitbucketIcon />
35+
Icon = BitbucketIcon
3736
break
3837
case "github":
3938
prettyName = "GitHub"
40-
icon = <GitHub />
39+
Icon = GitHub
4140
break
4241
case "gitlab":
4342
prettyName = "GitLab"
44-
icon = <GitlabIcon />
43+
Icon = GitlabIcon
4544
break
45+
default:
46+
throw new Error("invalid git provider: " + type)
4647
}
47-
const location = useLocation()
48-
const redirectURL = `${authenticateURL}?redirect=${location.pathname}`
48+
// The Git auth page uses a BroadcastChannel to notify listening
49+
// pages for Git auth updates if the "notify" query parameter is specified.
50+
const redirectURL = `${authenticateURL}?redirect=${encodeURIComponent(
51+
`/gitauth?notify`,
52+
)}`
4953

5054
return (
51-
<Tooltip title={authenticated ? "You're already authenticated!" : ""}>
52-
<a href={redirectURL} className={styles.link}>
53-
<Button disabled={authenticated}>
54-
<div className={styles.root}>
55-
{icon}
56-
{authenticated && <Typography variant="body2">Authenticated with {prettyName}</Typography>}
57-
{!authenticated && (
58-
<Typography variant="body2">Authenticate with {prettyName}</Typography>
59-
)}
60-
</div>
61-
</Button>
62-
</a>
55+
<Tooltip
56+
title={
57+
authenticated ? "You're already authenticated! No action needed." : ``
58+
}
59+
>
60+
<a
61+
href={redirectURL}
62+
className={styles.link}
63+
onClick={(event) => {
64+
event.preventDefault()
65+
// If the user is already authenticated, we don't want to redirect them
66+
if (authenticated || authenticateURL === "") {
67+
return
68+
}
69+
window.open(redirectURL, "_blank", "width=900,height=600")
70+
}}
71+
>
72+
<Button className={styles.button} disabled={authenticated} fullWidth>
73+
<div className={styles.root}>
74+
<Icon className={styles.icon} />
75+
<div className={styles.text}>
76+
<Typography variant="body2">
77+
{authenticated
78+
? `You're authenticated with ${prettyName}!`
79+
: `Click to login with ${prettyName}!`}
80+
</Typography>
81+
{!authenticated && (
82+
<Typography variant="caption" color="textSecondary">
83+
{"You'll be redirected back here once authenticated."}
84+
</Typography>
85+
)}
86+
</div>
87+
</div>
88+
</Button>
89+
</a>
6390
</Tooltip>
64-
6591
)
6692
}
6793

@@ -70,8 +96,22 @@ const useStyles = makeStyles(() => ({
7096
textDecoration: "none",
7197
},
7298
root: {
99+
padding: 4,
73100
display: "flex",
74-
gap: 16,
101+
gap: 12,
75102
alignItems: "center",
103+
textAlign: "left",
104+
},
105+
button: {
106+
height: "unset",
107+
},
108+
text: {
109+
display: "flex",
110+
flexDirection: "column",
111+
gap: 4,
112+
},
113+
icon: {
114+
width: 32,
115+
height: 32,
76116
},
77117
}))

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ const CreateWorkspacePage: FC = () => {
6969
[CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]:
7070
getTemplateSchemaError,
7171
[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]: createWorkspaceError,
72-
[CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR]: getTemplateGitAuthError,
72+
[CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR]:
73+
getTemplateGitAuthError,
7374
}}
7475
canCreateForUser={permissions?.createWorkspaceForUser}
7576
owner={owner}

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,25 @@ RichParameters.args = {
169169
],
170170
createWorkspaceErrors: {},
171171
}
172+
173+
export const GitAuth = Template.bind({})
174+
GitAuth.args = {
175+
templates: [MockTemplate],
176+
selectedTemplate: MockTemplate,
177+
createWorkspaceErrors: {},
178+
templateParameters: [],
179+
templateGitAuth: [
180+
{
181+
id: "github",
182+
type: "github",
183+
authenticated: false,
184+
authenticate_url: "",
185+
},
186+
{
187+
id: "gitlab",
188+
type: "gitlab",
189+
authenticated: true,
190+
authenticate_url: "",
191+
},
192+
],
193+
}

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,14 @@ export const CreateWorkspacePageView: FC<
231231
Git Authentication
232232
</h2>
233233
<p className={styles.formSectionInfoDescription}>
234-
This workspace requires authenticating with Git providers to
235-
create a workspace.
234+
This template requires authentication to automatically
235+
perform Git operations on create.
236236
</p>
237237
</div>
238238

239239
<Stack
240240
direction="column"
241-
spacing={4}
241+
spacing={2}
242242
className={styles.formSectionFields}
243243
>
244244
{props.templateGitAuth.map((auth, index) => (

site/src/pages/GitAuthPage/GitAuthPage.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,26 @@ import Button from "@material-ui/core/Button"
22
import { makeStyles } from "@material-ui/core/styles"
33
import { SignInLayout } from "components/SignInLayout/SignInLayout"
44
import { Welcome } from "components/Welcome/Welcome"
5-
import { FC } from "react"
6-
import { Link as RouterLink } from "react-router-dom"
5+
import { FC, useEffect } from "react"
6+
import { Link as RouterLink, useSearchParams } from "react-router-dom"
7+
import { REFRESH_GITAUTH_BROADCAST_CHANNEL } from "xServices/createWorkspace/createWorkspaceXService"
78

89
const GitAuthPage: FC = () => {
910
const styles = useStyles()
11+
const [searchParams] = useSearchParams()
12+
const shouldNotify = searchParams.has("notify")
13+
useEffect(() => {
14+
if (!shouldNotify) {
15+
return
16+
}
17+
18+
// This is used to notify the parent window that the Git auth token has been refreshed.
19+
// It's critical in the create workspace flow!
20+
const bc = new BroadcastChannel(REFRESH_GITAUTH_BROADCAST_CHANNEL)
21+
// The message doesn't matter, any message refreshes the page!
22+
bc.postMessage("noop")
23+
window.close()
24+
}, [shouldNotify])
1025

1126
return (
1227
<SignInLayout>

0 commit comments

Comments
 (0)