Skip to content

Commit a4258c7

Browse files
committed
setup with github oauth2
1 parent dab3105 commit a4258c7

File tree

3 files changed

+61
-6
lines changed

3 files changed

+61
-6
lines changed

coderd/userauth.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/coder/coder/v2/coderd/cryptokeys"
2828
"github.com/coder/coder/v2/coderd/idpsync"
2929
"github.com/coder/coder/v2/coderd/jwtutils"
30+
"github.com/coder/coder/v2/coderd/telemetry"
3031
"github.com/coder/coder/v2/coderd/util/ptr"
3132

3233
"github.com/coder/coder/v2/coderd/apikey"
@@ -1632,6 +1633,18 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
16321633
return xerrors.Errorf("unable to fetch default organization: %w", err)
16331634
}
16341635

1636+
// nolint:gocritic // Getting user count is a system function.
1637+
userCount, err := api.Database.GetUserCount(dbauthz.AsSystemRestricted(ctx))
1638+
if err != nil {
1639+
return xerrors.Errorf("unable to fetch user count: %w", err)
1640+
}
1641+
1642+
rbacRoles := []string{}
1643+
// If this is the first user, add the owner role.
1644+
if userCount == 0 {
1645+
rbacRoles = append(rbacRoles, rbac.RoleOwner().String())
1646+
}
1647+
16351648
//nolint:gocritic
16361649
user, err = api.CreateUser(dbauthz.AsSystemRestricted(ctx), tx, CreateUserRequest{
16371650
CreateUserRequestWithOrgs: codersdk.CreateUserRequestWithOrgs{
@@ -1646,10 +1659,20 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
16461659
},
16471660
LoginType: params.LoginType,
16481661
accountCreatorName: "oauth",
1662+
RBACRoles: rbacRoles,
16491663
})
16501664
if err != nil {
16511665
return xerrors.Errorf("create user: %w", err)
16521666
}
1667+
1668+
if userCount == 0 {
1669+
telemetryUser := telemetry.ConvertUser(user)
1670+
// The email is not anonymized for the first user.
1671+
telemetryUser.Email = &user.Email
1672+
api.Telemetry.Report(&telemetry.Snapshot{
1673+
Users: []telemetry.User{telemetryUser},
1674+
})
1675+
}
16531676
}
16541677

16551678
// Activate dormant user on sign-in

coderd/users.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) {
118118
// @Success 201 {object} codersdk.CreateFirstUserResponse
119119
// @Router /users/first [post]
120120
func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
121+
// The first user can also be created via oidc, so if making changes to the flow,
122+
// ensure that the oidc flow is also updated.
121123
ctx := r.Context()
122124
var createUser codersdk.CreateFirstUserRequest
123125
if !httpapi.Read(ctx, rw, r, &createUser) {
@@ -218,9 +220,12 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
218220
api.Logger.Debug(ctx, "entitlements will not be refreshed")
219221
}
220222

223+
rbacRoles := []string{rbac.RoleOwner().String()}
224+
221225
telemetryUser := telemetry.ConvertUser(user)
222226
// Send the initial users email address!
223227
telemetryUser.Email = &user.Email
228+
telemetryUser.RBACRoles = rbacRoles
224229
api.Telemetry.Report(&telemetry.Snapshot{
225230
Users: []telemetry.User{telemetryUser},
226231
})
@@ -231,7 +236,7 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
231236
// Add the admin role to this first user.
232237
//nolint:gocritic // needed to create first user
233238
_, err = api.Database.UpdateUserRoles(dbauthz.AsSystemRestricted(ctx), database.UpdateUserRolesParams{
234-
GrantedRoles: []string{rbac.RoleOwner().String()},
239+
GrantedRoles: rbacRoles,
235240
ID: user.ID,
236241
})
237242
if err != nil {
@@ -1345,6 +1350,7 @@ type CreateUserRequest struct {
13451350
LoginType database.LoginType
13461351
SkipNotifications bool
13471352
accountCreatorName string
1353+
RBACRoles []string
13481354
}
13491355

13501356
func (api *API) CreateUser(ctx context.Context, store database.Store, req CreateUserRequest) (database.User, error) {
@@ -1354,6 +1360,13 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
13541360
return database.User{}, xerrors.Errorf("invalid username %q: %w", req.Username, usernameValid)
13551361
}
13561362

1363+
// If the caller didn't specify rbac roles, default to
1364+
// a member of the site.
1365+
rbacRoles := []string{}
1366+
if req.RBACRoles != nil {
1367+
rbacRoles = req.RBACRoles
1368+
}
1369+
13571370
var user database.User
13581371
err := store.InTx(func(tx database.Store) error {
13591372
orgRoles := make([]string, 0)
@@ -1370,10 +1383,9 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
13701383
CreatedAt: dbtime.Now(),
13711384
UpdatedAt: dbtime.Now(),
13721385
HashedPassword: []byte{},
1373-
// All new users are defaulted to members of the site.
1374-
RBACRoles: []string{},
1375-
LoginType: req.LoginType,
1376-
Status: status,
1386+
RBACRoles: rbacRoles,
1387+
LoginType: req.LoginType,
1388+
Status: status,
13771389
}
13781390
// If a user signs up with OAuth, they can have no password!
13791391
if req.Password != "" {

site/src/pages/SetupPage/SetupPageView.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import GitHubIcon from "@mui/icons-material/GitHub";
12
import LoadingButton from "@mui/lab/LoadingButton";
23
import AlertTitle from "@mui/material/AlertTitle";
34
import Autocomplete from "@mui/material/Autocomplete";
5+
import Button from "@mui/material/Button";
46
import Checkbox from "@mui/material/Checkbox";
57
import Link from "@mui/material/Link";
68
import MenuItem from "@mui/material/MenuItem";
@@ -16,7 +18,6 @@ import { SignInLayout } from "components/SignInLayout/SignInLayout";
1618
import { Stack } from "components/Stack/Stack";
1719
import { type FormikContextType, useFormik } from "formik";
1820
import type { FC } from "react";
19-
import { useEffect } from "react";
2021
import { docs } from "utils/docs";
2122
import {
2223
getFormHelpers,
@@ -34,6 +35,7 @@ export const Language = {
3435
emailRequired: "Please enter an email address.",
3536
passwordRequired: "Please enter a password.",
3637
create: "Create account",
38+
githubCreate: "Create account with GitHub",
3739
welcomeMessage: <>Welcome to Coder</>,
3840
firstNameLabel: "First name",
3941
lastNameLabel: "Last name",
@@ -81,6 +83,11 @@ const numberOfDevelopersOptions = [
8183
"2500+",
8284
];
8385

86+
const iconStyles = {
87+
width: 16,
88+
height: 16,
89+
};
90+
8491
export interface SetupPageViewProps {
8592
onSubmit: (firstUser: TypesGen.CreateFirstUserRequest) => void;
8693
error?: unknown;
@@ -140,6 +147,19 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
140147
Let&lsquo;s create your first admin user account
141148
</div>
142149
</header>
150+
<div className="mb-8">
151+
<Button
152+
component="a"
153+
href="/api/v2/users/oauth2/github/callback"
154+
variant="contained"
155+
startIcon={<GitHubIcon css={iconStyles} />}
156+
fullWidth
157+
type="submit"
158+
size="xlarge"
159+
>
160+
{Language.githubCreate}
161+
</Button>
162+
</div>
143163
<VerticalForm onSubmit={form.handleSubmit}>
144164
<FormFields>
145165
<TextField

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy