Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAuth2 auto-register #5123

Merged
merged 46 commits into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a3366c4
Refactored handleOAuth2SignIn in routers/user/auth.go
mgjm Oct 19, 2018
6e2ece4
Refactored user creation
mgjm Oct 19, 2018
a473815
Added auto-register for OAuth2 users
mgjm Oct 19, 2018
a700b02
Moved oauth2 settings to new section in app.ini
mgjm Oct 23, 2018
f0124c4
Added oauth2 use nickname setting
mgjm Oct 23, 2018
9504a74
Merge branch 'master' into oauth2-auto-register
lafriks Oct 30, 2018
688afbd
Merge branch 'master' into oauth2-auto-register
mgjm Feb 24, 2020
aeb833c
Moved oauth2_client settings to service file
mgjm Feb 25, 2020
527c7e6
Updated comments on auth helpers
mgjm Feb 25, 2020
6a3a0e7
Renamed oauth2_client settings
mgjm Feb 25, 2020
7a84789
Uncommented setting in app.ini.sample
mgjm Apr 1, 2020
b57cb72
Merge branch 'master' into oauth2-auto-register
mgjm Apr 1, 2020
3f93e42
Merge branch 'master' into oauth2-auto-register
6543 Jan 26, 2021
6fe0f39
fix conflict resolve relict
6543 Jan 26, 2021
c39ed5a
Merge branch 'master' into oauth2-auto-register
6543 Jan 26, 2021
bcd3fb6
Merge branch 'master' into oauth2-auto-register
6543 Feb 4, 2021
9d1e1f2
Added error for missing fields in OAuth2 response
mgjm Feb 7, 2021
8a64dfe
Fixed error handling in createUserInContext
mgjm Feb 7, 2021
aece370
Merge branch 'master' into oauth2-auto-register
6543 Feb 7, 2021
1373b78
Merge branch 'master' into oauth2-auto-register
6543 Feb 7, 2021
8ee4097
Merge branch 'master' into oauth2-auto-register
6543 Feb 9, 2021
7b2ff29
Merge branch 'master' into oauth2-auto-register
6543 Feb 18, 2021
e166d49
Merge branch 'master' into oauth2-auto-register
6543 Feb 21, 2021
4cc2d8f
Merge branch 'master' into oauth2-auto-register
kvaster Mar 26, 2021
3edad74
Linking and auto linking on oauth2 registration
kvaster Mar 26, 2021
7a811af
Code cleanup
kvaster Mar 30, 2021
150163e
Fix lint problems
kvaster Mar 30, 2021
586dff7
Fix bugs in validating config options
kvaster Mar 30, 2021
fb464db
Convert oauth2 client types to string enums
kvaster Mar 30, 2021
d62cf15
Fix ioutil.ReadAll
mgjm Mar 30, 2021
f4924a3
Set default username source to nickname
mgjm Mar 31, 2021
28b696c
Merge branch 'master' into oauth2-auto-register
mgjm Mar 31, 2021
491610a
Add copyright and empty line
mgjm Apr 1, 2021
cd7241e
Move oauth2 client settings to new file
mgjm Apr 3, 2021
a3cf72b
Add automatic oauth2 scopes for github and google
mgjm Apr 3, 2021
3d99ee8
Add hint to change the openid connect scopes if fields are missing
mgjm Apr 3, 2021
917ed54
Merge branch 'master' into oauth2-auto-register
mgjm Apr 3, 2021
ebc0d0b
OAuth2 sign in is not handled properly after all merges
kvaster Apr 4, 2021
7795946
Merge branch 'master' into oauth2-auto-register
mgjm Apr 6, 2021
d448ed9
Merge branch 'master' into oauth2-auto-register
6543 Apr 8, 2021
1d4d7d3
Merge branch 'master' into oauth2-auto-register
6543 Apr 10, 2021
8f44610
Merge branch 'master' into oauth2-auto-register
6543 Apr 11, 2021
f320e34
More detailed description of options in cheat sheet
kvaster Apr 13, 2021
2987081
Correct info about auto linking security risk
kvaster Apr 13, 2021
7b96396
Extend info about auto linking security risk
mgjm Apr 14, 2021
4de0b71
Merge branch 'master' into oauth2-auto-register
6543 Apr 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,30 @@ WHITELISTED_URIS =
; Example value: loadaverage.org/badguy stackexchange.com/.*spammer
BLACKLISTED_URIS =

[oauth2_client]
; Whether a new auto registered oauth2 user needs to confirm their email.
; Do not include to use the REGISTER_EMAIL_CONFIRM setting from the `[service]` section.
REGISTER_EMAIL_CONFIRM =
; Scopes for the openid connect oauth2 provider (seperated by space, the openid scope is implicitly added).
; Typical values are profile and email.
; For more information about the possible values see https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
OPENID_CONNECT_SCOPES =
; Automatically create user accounts for new oauth2 users.
ENABLE_AUTO_REGISTRATION = false
; The source of the username for new oauth2 accounts:
; userid = use the userid / sub attribute
; nickname = use the nickname attribute
; email = use the username part of the email attribute
USERNAME = nickname
; Update avatar if available from oauth2 provider.
; Update will be performed on each login.
UPDATE_AVATAR = false
; How to handle if an account / email already exists:
; disabled = show an error
; login = show an account linking login
; auto = link directly with the account
ACCOUNT_LINKING = disabled

[service]
; Time limit to confirm account/email registration
ACTIVE_CODE_LIVE_MINUTES = 180
Expand Down
15 changes: 15 additions & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,21 @@ relation to port exhaustion.
- `BLACKLISTED_URIS`: **\<empty\>**: If non-empty, list of POSIX regex patterns matching
OpenID URI's to block.

## OAuth2 Client (`oauth2_client`)

- `REGISTER_EMAIL_CONFIRM`: *[service]* **REGISTER\_EMAIL\_CONFIRM**: Set this to enable or disable email confirmation of OAuth2 auto-registration. (Overwrites the REGISTER\_EMAIL\_CONFIRM setting of the `[service]` section)
- `OPENID_CONNECT_SCOPES`: **\<empty\>**: List of additional openid connect scopes. (`openid` is implicitly added)
- `ENABLE_AUTO_REGISTRATION`: **false**: Automatically create user accounts for new oauth2 users.
- `USERNAME`: **nickname**: The source of the username for new oauth2 accounts:
- userid - use the userid / sub attribute
- nickname - use the nickname attribute
- email - use the username part of the email attribute
- `UPDATE_AVATAR`: **false**: Update avatar if available from oauth2 provider. Update will be performed on each login.
- `ACCOUNT_LINKING`: **disabled**: How to handle if an account / email already exists:
- disabled - show an error
- login - show an account linking login
- auto - automatically link with the account (Please be aware that this will grant access to an existing account just because the same username or email is provided. You must make sure that this does not cause issues with your authentication providers.)

## Service (`service`)

- `ACTIVE_CODE_LIVE_MINUTES`: **180**: Time limit (min) to confirm account/email registration.
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ var migrations = []Migration{

// v178 -> v179
NewMigration("Add LFS columns to Mirror", addLFSMirrorColumns),
// v179 -> v180
NewMigration("Convert avatar url to text", convertAvatarURLToText),
}

// GetCurrentDBVersion returns the current db version
Expand Down
26 changes: 26 additions & 0 deletions models/migrations/v179.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 migrations

import (
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)

func convertAvatarURLToText(x *xorm.Engine) error {
dbType := x.Dialect().URI().DBType
if dbType == schemas.SQLITE { // For SQLITE, varchar or char will always be represented as TEXT
return nil
}

// Some oauth2 providers may give very long avatar urls (i.e. Google)
return modifyColumn(x, "external_login_user", &schemas.Column{
Name: "avatar_url",
SQLType: schemas.SQLType{
Name: schemas.Text,
},
Nullable: true,
})
}
14 changes: 11 additions & 3 deletions modules/auth/oauth2/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,11 @@ func createProvider(providerName, providerType, clientID, clientSecret, openIDCo
emailURL = customURLMapping.EmailURL
}
}
provider = github.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL, emailURL)
scopes := []string{}
if setting.OAuth2Client.EnableAutoRegistration {
scopes = append(scopes, "user:email")
}
provider = github.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL, emailURL, scopes...)
case "gitlab":
authURL := gitlab.AuthURL
tokenURL := gitlab.TokenURL
Expand All @@ -175,9 +179,13 @@ func createProvider(providerName, providerType, clientID, clientSecret, openIDCo
}
provider = gitlab.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL, "read_user")
case "gplus": // named gplus due to legacy gplus -> google migration (Google killed Google+). This ensures old connections still work
provider = google.New(clientID, clientSecret, callbackURL)
scopes := []string{"email"}
if setting.OAuth2Client.UpdateAvatar || setting.OAuth2Client.EnableAutoRegistration {
scopes = append(scopes, "profile")
}
provider = google.New(clientID, clientSecret, callbackURL, scopes...)
case "openidConnect":
if provider, err = openidConnect.New(clientID, clientSecret, callbackURL, openIDConnectAutoDiscoveryURL); err != nil {
if provider, err = openidConnect.New(clientID, clientSecret, callbackURL, openIDConnectAutoDiscoveryURL, setting.OAuth2Client.OpenIDConnectScopes...); err != nil {
log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, openIDConnectAutoDiscoveryURL, err)
}
case "twitter":
Expand Down
90 changes: 90 additions & 0 deletions modules/setting/oauth2_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 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
}
1 change: 1 addition & 0 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,7 @@ func MakeManifestData(appName string, appURL string, absoluteAssetURL string) []
func NewServices() {
InitDBConfig()
newService()
newOAuth2Client()
NewLogServices(false)
newCacheService()
newSessionService()
Expand Down
Loading