mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
feat: assign users to groups returned by OIDC provider (#5965)
This commit is contained in:
@ -311,6 +311,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
|
||||
if ok {
|
||||
username, _ = usernameRaw.(string)
|
||||
}
|
||||
|
||||
emailRaw, ok := claims["email"]
|
||||
if !ok {
|
||||
// Email is an optional claim in OIDC and
|
||||
@ -326,6 +327,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
emailRaw = username
|
||||
}
|
||||
|
||||
email, ok := emailRaw.(string)
|
||||
if !ok {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
@ -333,6 +335,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
verifiedRaw, ok := claims["email_verified"]
|
||||
if ok {
|
||||
verified, ok := verifiedRaw.(bool)
|
||||
@ -346,6 +349,26 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
|
||||
api.Logger.Warn(ctx, "allowing unverified oidc email %q")
|
||||
}
|
||||
}
|
||||
|
||||
var groups []string
|
||||
groupsRaw, ok := claims["groups"]
|
||||
if ok {
|
||||
// Convert the []interface{} we get to a []string.
|
||||
groupsInterface, ok := groupsRaw.([]interface{})
|
||||
if ok {
|
||||
for _, groupInterface := range groupsInterface {
|
||||
group, ok := groupInterface.(string)
|
||||
if !ok {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: fmt.Sprintf("Invalid group type. Expected string, got: %t", emailRaw),
|
||||
})
|
||||
return
|
||||
}
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The username is a required property in Coder. We make a best-effort
|
||||
// attempt at using what the claims provide, but if that fails we will
|
||||
// generate a random username.
|
||||
@ -359,6 +382,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
username = httpapi.UsernameFrom(username)
|
||||
}
|
||||
|
||||
if len(api.OIDCConfig.EmailDomain) > 0 {
|
||||
ok = false
|
||||
for _, domain := range api.OIDCConfig.EmailDomain {
|
||||
@ -374,6 +398,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var picture string
|
||||
pictureRaw, ok := claims["picture"]
|
||||
if ok {
|
||||
@ -388,6 +413,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
|
||||
Email: email,
|
||||
Username: username,
|
||||
AvatarURL: picture,
|
||||
Groups: groups,
|
||||
})
|
||||
var httpErr httpError
|
||||
if xerrors.As(err, &httpErr) {
|
||||
@ -425,6 +451,7 @@ type oauthLoginParams struct {
|
||||
Email string
|
||||
Username string
|
||||
AvatarURL string
|
||||
Groups []string
|
||||
}
|
||||
|
||||
type httpError struct {
|
||||
@ -546,22 +573,6 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
|
||||
}
|
||||
}
|
||||
|
||||
// LEGACY: Remove 10/2022.
|
||||
// We started tracking linked IDs later so it's possible for a user to be a
|
||||
// pre-existing OAuth user and not have a linked ID.
|
||||
// The migration that added the user_links table could not populate
|
||||
// the 'linked_id' field since it requires fields off the access token.
|
||||
if link.LinkedID == "" {
|
||||
link, err = tx.UpdateUserLinkedID(ctx, database.UpdateUserLinkedIDParams{
|
||||
UserID: user.ID,
|
||||
LoginType: params.LoginType,
|
||||
LinkedID: params.LinkedID,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("update user linked ID: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if link.UserID != uuid.Nil {
|
||||
link, err = tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
||||
UserID: user.ID,
|
||||
@ -575,6 +586,14 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure groups are correct.
|
||||
if len(params.Groups) > 0 {
|
||||
err := api.Options.SetUserGroups(ctx, tx, user.ID, params.Groups)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("set user groups: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
needsUpdate := false
|
||||
if user.AvatarURL.String != params.AvatarURL {
|
||||
user.AvatarURL = sql.NullString{
|
||||
|
Reference in New Issue
Block a user