Skip to content

Commit 5fb6932

Browse files
committed
setup with github oauth2
1 parent dab3105 commit 5fb6932

File tree

3 files changed

+61
-5
lines changed

3 files changed

+61
-5
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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { SignInLayout } from "components/SignInLayout/SignInLayout";
1616
import { Stack } from "components/Stack/Stack";
1717
import { type FormikContextType, useFormik } from "formik";
1818
import type { FC } from "react";
19+
import Button from "@mui/material/Button";
20+
import GitHubIcon from "@mui/icons-material/GitHub";
1921
import { useEffect } from "react";
2022
import { docs } from "utils/docs";
2123
import {
@@ -34,6 +36,7 @@ export const Language = {
3436
emailRequired: "Please enter an email address.",
3537
passwordRequired: "Please enter a password.",
3638
create: "Create account",
39+
githubCreate: "Create account with GitHub",
3740
welcomeMessage: <>Welcome to Coder</>,
3841
firstNameLabel: "First name",
3942
lastNameLabel: "Last name",
@@ -81,6 +84,11 @@ const numberOfDevelopersOptions = [
8184
"2500+",
8285
];
8386

87+
const iconStyles = {
88+
width: 16,
89+
height: 16,
90+
};
91+
8492
export interface SetupPageViewProps {
8593
onSubmit: (firstUser: TypesGen.CreateFirstUserRequest) => void;
8694
error?: unknown;
@@ -140,6 +148,19 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
140148
Let&lsquo;s create your first admin user account
141149
</div>
142150
</header>
151+
<div className="mb-8">
152+
<Button
153+
component="a"
154+
href="/api/v2/users/oauth2/github/callback"
155+
variant="contained"
156+
startIcon={<GitHubIcon css={iconStyles} />}
157+
fullWidth
158+
type="submit"
159+
size="xlarge"
160+
>
161+
{Language.githubCreate}
162+
</Button>
163+
</div>
143164
<VerticalForm onSubmit={form.handleSubmit}>
144165
<FormFields>
145166
<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