forgejo/modules/setting/oauth2_client.go
Martin Michaelis 55eb1745bd
OAuth2 auto-register (#5123)
* Refactored handleOAuth2SignIn in routers/user/auth.go

The function handleOAuth2SignIn was called twice but some code path could only
be reached by one of the invocations. Moved the unnecessary code path out of
handleOAuth2SignIn.


* Refactored user creation

There was common code to create a user and display the correct error message.
And after the creation the only user should be an admin and if enabled a
confirmation email should be sent. This common code is now abstracted into
two functions and a helper function to call both.

* Added auto-register for OAuth2 users

If enabled new OAuth2 users will be registered with their OAuth2 details.
The UserID, Name and Email fields from the gothUser are used.
Therefore the OpenID Connect provider needs additional scopes to return
the coresponding claims.

* Added error for missing fields in OAuth2 response

* Linking and auto linking on oauth2 registration

* Set default username source to nickname

* Add automatic oauth2 scopes for github and google

* Add hint to change the openid connect scopes if fields are missing

* Extend info about auto linking security risk

Co-authored-by: Viktor Kuzmin <kvaster@gmail.com>
Signed-off-by: Martin Michaelis <code@mgjm.de>
2021-04-14 14:02:12 +02:00

90 lines
3.3 KiB
Go

// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package setting
import (
"code.gitea.io/gitea/modules/log"
"gopkg.in/ini.v1"
)
// OAuth2UsernameType is enum describing the way gitea 'name' should be generated from oauth2 data
type OAuth2UsernameType string
const (
// OAuth2UsernameUserid oauth2 userid field will be used as gitea name
OAuth2UsernameUserid OAuth2UsernameType = "userid"
// OAuth2UsernameNickname oauth2 nickname field will be used as gitea name
OAuth2UsernameNickname OAuth2UsernameType = "nickname"
// OAuth2UsernameEmail username of oauth2 email filed will be used as gitea name
OAuth2UsernameEmail OAuth2UsernameType = "email"
)
func (username OAuth2UsernameType) isValid() bool {
switch username {
case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail:
return true
}
return false
}
// OAuth2AccountLinkingType is enum describing behaviour of linking with existing account
type OAuth2AccountLinkingType string
const (
// OAuth2AccountLinkingDisabled error will be displayed if account exist
OAuth2AccountLinkingDisabled OAuth2AccountLinkingType = "disabled"
// OAuth2AccountLinkingLogin account linking login will be displayed if account exist
OAuth2AccountLinkingLogin OAuth2AccountLinkingType = "login"
// OAuth2AccountLinkingAuto account will be automatically linked if account exist
OAuth2AccountLinkingAuto OAuth2AccountLinkingType = "auto"
)
func (accountLinking OAuth2AccountLinkingType) isValid() bool {
switch accountLinking {
case OAuth2AccountLinkingDisabled, OAuth2AccountLinkingLogin, OAuth2AccountLinkingAuto:
return true
}
return false
}
// OAuth2Client settings
var OAuth2Client struct {
RegisterEmailConfirm bool
OpenIDConnectScopes []string
EnableAutoRegistration bool
Username OAuth2UsernameType
UpdateAvatar bool
AccountLinking OAuth2AccountLinkingType
}
func newOAuth2Client() {
sec := Cfg.Section("oauth2_client")
OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm)
OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES")
OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool()
OAuth2Client.Username = OAuth2UsernameType(sec.Key("USERNAME").MustString(string(OAuth2UsernameNickname)))
if !OAuth2Client.Username.isValid() {
log.Warn("Username setting is not valid: '%s', will fallback to '%s'", OAuth2Client.Username, OAuth2UsernameNickname)
OAuth2Client.Username = OAuth2UsernameNickname
}
OAuth2Client.UpdateAvatar = sec.Key("UPDATE_AVATAR").MustBool()
OAuth2Client.AccountLinking = OAuth2AccountLinkingType(sec.Key("ACCOUNT_LINKING").MustString(string(OAuth2AccountLinkingDisabled)))
if !OAuth2Client.AccountLinking.isValid() {
log.Warn("Account linking setting is not valid: '%s', will fallback to '%s'", OAuth2Client.AccountLinking, OAuth2AccountLinkingDisabled)
OAuth2Client.AccountLinking = OAuth2AccountLinkingDisabled
}
}
func parseScopes(sec *ini.Section, name string) []string {
parts := sec.Key(name).Strings(" ")
scopes := make([]string, 0, len(parts))
for _, scope := range parts {
if scope != "" {
scopes = append(scopes, scope)
}
}
return scopes
}