@@ -7,13 +7,21 @@ import KeyIcon from "@mui/icons-material/VpnKey";
7
7
import Button from "@mui/material/Button" ;
8
8
import Typography from "@mui/material/Typography" ;
9
9
import { convertToOAUTH } from "api/api" ;
10
- import { AuthMethods , LoginType , UserLoginType } from "api/typesGenerated" ;
11
- import Skeleton from "@mui/material/Skeleton" ;
10
+ import {
11
+ AuthMethods ,
12
+ LoginType ,
13
+ OIDCAuthMethod ,
14
+ UserLoginType ,
15
+ } from "api/typesGenerated" ;
12
16
import { Stack } from "components/Stack/Stack" ;
13
17
import { useMutation } from "@tanstack/react-query" ;
14
18
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog" ;
15
19
import { getErrorMessage } from "api/errors" ;
16
20
import CheckCircleOutlined from "@mui/icons-material/CheckCircleOutlined" ;
21
+ import { EmptyState } from "components/EmptyState/EmptyState" ;
22
+ import { makeStyles } from "@mui/styles" ;
23
+ import Link from "@mui/material/Link" ;
24
+ import { docs } from "utils/docs" ;
17
25
18
26
type LoginTypeConfirmation =
19
27
| {
@@ -93,6 +101,32 @@ export const useSingleSignOnSection = () => {
93
101
} ;
94
102
} ;
95
103
104
+ const useEmptyStateStyles = makeStyles ( ( theme ) => ( {
105
+ root : {
106
+ minHeight : 0 ,
107
+ padding : theme . spacing ( 6 , 4 ) ,
108
+ backgroundColor : theme . palette . background . paper ,
109
+ borderRadius : theme . shape . borderRadius ,
110
+ } ,
111
+ } ) ) ;
112
+
113
+ function SSOEmptyState ( ) {
114
+ const styles = useEmptyStateStyles ( ) ;
115
+
116
+ return (
117
+ < EmptyState
118
+ className = { styles . root }
119
+ message = "No SSO Providers"
120
+ description = "No SSO providers are configured with this Coder deployment."
121
+ cta = {
122
+ < Link href = { docs ( "/admin/auth" ) } target = "_blank" rel = "noreferrer" >
123
+ Learn how to add a provider
124
+ </ Link >
125
+ }
126
+ />
127
+ ) ;
128
+ }
129
+
96
130
type SingleSignOnSectionProps = ReturnType < typeof useSingleSignOnSection > & {
97
131
authMethods : AuthMethods ;
98
132
userLoginType : UserLoginType ;
@@ -108,6 +142,12 @@ export const SingleSignOnSection = ({
108
142
isConfirming,
109
143
error,
110
144
} : SingleSignOnSectionProps ) => {
145
+ const authList = Object . values (
146
+ authMethods ,
147
+ ) as ( typeof authMethods ) [ keyof typeof authMethods ] [ ] ;
148
+
149
+ const noSsoEnabled = ! authList . some ( ( method ) => method . enabled ) ;
150
+
111
151
return (
112
152
< >
113
153
< Section
@@ -116,73 +156,69 @@ export const SingleSignOnSection = ({
116
156
description = "Authenticate in Coder using one-click"
117
157
>
118
158
< Box display = "grid" gap = "16px" >
119
- { authMethods && userLoginType ? (
120
- userLoginType . login_type === "password" ? (
121
- < >
122
- { authMethods . github . enabled && (
123
- < Button
124
- disabled = { isUpdating }
125
- onClick = { ( ) => openConfirmation ( "github" ) }
126
- startIcon = { < GitHubIcon sx = { { width : 16 , height : 16 } } /> }
127
- fullWidth
128
- size = "large"
129
- >
130
- GitHub
131
- </ Button >
132
- ) }
133
- { authMethods . oidc . enabled && (
134
- < Button
135
- size = "large"
136
- startIcon = { < OIDCIcon authMethods = { authMethods } /> }
137
- fullWidth
138
- disabled = { isUpdating }
139
- onClick = { ( ) => openConfirmation ( "oidc" ) }
140
- >
141
- { getOIDCLabel ( authMethods ) }
142
- </ Button >
143
- ) }
144
- </ >
145
- ) : (
146
- < Box
159
+ { userLoginType . login_type === "password" ? (
160
+ < >
161
+ { authMethods . github . enabled && (
162
+ < Button
163
+ size = "large"
164
+ fullWidth
165
+ disabled = { isUpdating }
166
+ startIcon = { < GitHubIcon sx = { { width : 16 , height : 16 } } /> }
167
+ onClick = { ( ) => openConfirmation ( "github" ) }
168
+ >
169
+ GitHub
170
+ </ Button >
171
+ ) }
172
+
173
+ { authMethods . oidc . enabled && (
174
+ < Button
175
+ size = "large"
176
+ fullWidth
177
+ disabled = { isUpdating }
178
+ startIcon = { < OIDCIcon oidcAuth = { authMethods . oidc } /> }
179
+ onClick = { ( ) => openConfirmation ( "oidc" ) }
180
+ >
181
+ { getOIDCLabel ( authMethods . oidc ) }
182
+ </ Button >
183
+ ) }
184
+
185
+ { noSsoEnabled && < SSOEmptyState /> }
186
+ </ >
187
+ ) : (
188
+ < Box
189
+ sx = { {
190
+ background : ( theme ) => theme . palette . background . paper ,
191
+ borderRadius : 1 ,
192
+ border : ( theme ) => `1px solid ${ theme . palette . divider } ` ,
193
+ padding : 2 ,
194
+ display : "flex" ,
195
+ gap : 2 ,
196
+ alignItems : "center" ,
197
+ fontSize : 14 ,
198
+ } }
199
+ >
200
+ < CheckCircleOutlined
147
201
sx = { {
148
- background : ( theme ) => theme . palette . background . paper ,
149
- borderRadius : 1 ,
150
- border : ( theme ) => `1px solid ${ theme . palette . divider } ` ,
151
- padding : 2 ,
152
- display : "flex" ,
153
- gap : 2 ,
154
- alignItems : "center" ,
155
- fontSize : 14 ,
202
+ color : ( theme ) => theme . palette . success . light ,
203
+ fontSize : 16 ,
156
204
} }
157
- >
158
- < CheckCircleOutlined
159
- sx = { {
160
- color : ( theme ) => theme . palette . success . light ,
161
- fontSize : 16 ,
162
- } }
163
- />
164
- < span >
165
- Authenticated with{ " " }
166
- < strong >
167
- { userLoginType . login_type === "github"
168
- ? "GitHub"
169
- : getOIDCLabel ( authMethods ) }
170
- </ strong >
171
- </ span >
172
- < Box sx = { { ml : "auto" , lineHeight : 1 } } >
173
- { userLoginType . login_type === "github" ? (
174
- < GitHubIcon sx = { { width : 16 , height : 16 } } />
175
- ) : (
176
- < OIDCIcon authMethods = { authMethods } />
177
- ) }
178
- </ Box >
205
+ />
206
+ < span >
207
+ Authenticated with{ " " }
208
+ < strong >
209
+ { userLoginType . login_type === "github"
210
+ ? "GitHub"
211
+ : getOIDCLabel ( authMethods . oidc ) }
212
+ </ strong >
213
+ </ span >
214
+ < Box sx = { { ml : "auto" , lineHeight : 1 } } >
215
+ { userLoginType . login_type === "github" ? (
216
+ < GitHubIcon sx = { { width : 16 , height : 16 } } />
217
+ ) : (
218
+ < OIDCIcon oidcAuth = { authMethods . oidc } />
219
+ ) }
179
220
</ Box >
180
- )
181
- ) : (
182
- < Skeleton
183
- variant = "rectangular"
184
- sx = { { height : 40 , borderRadius : 1 } }
185
- />
221
+ </ Box >
186
222
) }
187
223
</ Box >
188
224
</ Section >
@@ -198,21 +234,23 @@ export const SingleSignOnSection = ({
198
234
) ;
199
235
} ;
200
236
201
- const OIDCIcon = ( { authMethods } : { authMethods : AuthMethods } ) => {
202
- return authMethods . oidc . iconUrl ? (
237
+ const OIDCIcon = ( { oidcAuth } : { oidcAuth : OIDCAuthMethod } ) => {
238
+ if ( ! oidcAuth . iconUrl ) {
239
+ return < KeyIcon sx = { { width : 16 , height : 16 } } /> ;
240
+ }
241
+
242
+ return (
203
243
< Box
204
244
component = "img"
205
245
alt = "Open ID Connect icon"
206
- src = { authMethods . oidc . iconUrl }
246
+ src = { oidcAuth . iconUrl }
207
247
sx = { { width : 16 , height : 16 } }
208
248
/>
209
- ) : (
210
- < KeyIcon sx = { { width : 16 , height : 16 } } />
211
249
) ;
212
250
} ;
213
251
214
- const getOIDCLabel = ( authMethods : AuthMethods ) => {
215
- return authMethods . oidc . signInText || "OpenID Connect" ;
252
+ const getOIDCLabel = ( oidcAuth : OIDCAuthMethod ) => {
253
+ return oidcAuth . signInText || "OpenID Connect" ;
216
254
} ;
217
255
218
256
const ConfirmLoginTypeChangeModal = ( {
0 commit comments