Browse Source

feat: Improve oauth redirect (#303)

* fix: redirects after oauth can potentially misalign with server's actually hostname

* feat: remove `RedirectURL` from oauth config, as it should checked by provider rather than client

* feat: align oauth endpoint with the hostname in requests
k3-cat 7 months ago
parent
commit
c52706e621

+ 5 - 4
cmd/apimain.go

@@ -2,6 +2,10 @@ package main
2
 
2
 
3
 import (
3
 import (
4
 	"fmt"
4
 	"fmt"
5
+	"os"
6
+	"strconv"
7
+	"time"
8
+
5
 	"github.com/go-redis/redis/v8"
9
 	"github.com/go-redis/redis/v8"
6
 	"github.com/lejianwen/rustdesk-api/v2/config"
10
 	"github.com/lejianwen/rustdesk-api/v2/config"
7
 	"github.com/lejianwen/rustdesk-api/v2/global"
11
 	"github.com/lejianwen/rustdesk-api/v2/global"
@@ -17,9 +21,6 @@ import (
17
 	"github.com/lejianwen/rustdesk-api/v2/utils"
21
 	"github.com/lejianwen/rustdesk-api/v2/utils"
18
 	"github.com/nicksnyder/go-i18n/v2/i18n"
22
 	"github.com/nicksnyder/go-i18n/v2/i18n"
19
 	"github.com/spf13/cobra"
23
 	"github.com/spf13/cobra"
20
-	"os"
21
-	"strconv"
22
-	"time"
23
 )
24
 )
24
 
25
 
25
 // @title 管理系统API
26
 // @title 管理系统API
@@ -210,7 +211,7 @@ func InitGlobal() {
210
 }
211
 }
211
 
212
 
212
 func DatabaseAutoUpdate() {
213
 func DatabaseAutoUpdate() {
213
-	version := 262
214
+	version := 263
214
 
215
 
215
 	db := global.DB
216
 	db := global.DB
216
 
217
 

+ 0 - 4
config/oauth.go

@@ -3,24 +3,20 @@ package config
3
 type GithubOauth struct {
3
 type GithubOauth struct {
4
 	ClientId     string `mapstructure:"client-id"`
4
 	ClientId     string `mapstructure:"client-id"`
5
 	ClientSecret string `mapstructure:"client-secret"`
5
 	ClientSecret string `mapstructure:"client-secret"`
6
-	RedirectUrl  string `mapstructure:"redirect-url"`
7
 }
6
 }
8
 
7
 
9
 type GoogleOauth struct {
8
 type GoogleOauth struct {
10
 	ClientId     string `mapstructure:"client-id"`
9
 	ClientId     string `mapstructure:"client-id"`
11
 	ClientSecret string `mapstructure:"client-secret"`
10
 	ClientSecret string `mapstructure:"client-secret"`
12
-	RedirectUrl  string `mapstructure:"redirect-url"`
13
 }
11
 }
14
 
12
 
15
 type OidcOauth struct {
13
 type OidcOauth struct {
16
 	Issuer       string `mapstructure:"issuer"`
14
 	Issuer       string `mapstructure:"issuer"`
17
 	ClientId     string `mapstructure:"client-id"`
15
 	ClientId     string `mapstructure:"client-id"`
18
 	ClientSecret string `mapstructure:"client-secret"`
16
 	ClientSecret string `mapstructure:"client-secret"`
19
-	RedirectUrl  string `mapstructure:"redirect-url"`
20
 }
17
 }
21
 
18
 
22
 type LinuxdoOauth struct {
19
 type LinuxdoOauth struct {
23
 	ClientId     string `mapstructure:"client-id"`
20
 	ClientId     string `mapstructure:"client-id"`
24
 	ClientSecret string `mapstructure:"client-secret"`
21
 	ClientSecret string `mapstructure:"client-secret"`
25
-	RedirectUrl  string `mapstructure:"redirect-url"`
26
 }
22
 }

+ 1 - 8
docs/admin/admin_docs.go

@@ -5569,8 +5569,7 @@ const docTemplateadmin = `{
5569
             "required": [
5569
             "required": [
5570
                 "client_id",
5570
                 "client_id",
5571
                 "client_secret",
5571
                 "client_secret",
5572
-                "oauth_type",
5573
-                "redirect_url"
5572
+                "oauth_type"
5574
             ],
5573
             ],
5575
             "properties": {
5574
             "properties": {
5576
                 "auto_register": {
5575
                 "auto_register": {
@@ -5600,9 +5599,6 @@ const docTemplateadmin = `{
5600
                 "pkce_method": {
5599
                 "pkce_method": {
5601
                     "type": "string"
5600
                     "type": "string"
5602
                 },
5601
                 },
5603
-                "redirect_url": {
5604
-                    "type": "string"
5605
-                },
5606
                 "scopes": {
5602
                 "scopes": {
5607
                     "type": "string"
5603
                     "type": "string"
5608
                 }
5604
                 }
@@ -6296,9 +6292,6 @@ const docTemplateadmin = `{
6296
                 "pkce_method": {
6292
                 "pkce_method": {
6297
                     "type": "string"
6293
                     "type": "string"
6298
                 },
6294
                 },
6299
-                "redirect_url": {
6300
-                    "type": "string"
6301
-                },
6302
                 "scopes": {
6295
                 "scopes": {
6303
                     "type": "string"
6296
                     "type": "string"
6304
                 },
6297
                 },

+ 2 - 9
docs/admin/admin_swagger.json

@@ -5562,8 +5562,7 @@
5562
             "required": [
5562
             "required": [
5563
                 "client_id",
5563
                 "client_id",
5564
                 "client_secret",
5564
                 "client_secret",
5565
-                "oauth_type",
5566
-                "redirect_url"
5565
+                "oauth_type"
5567
             ],
5566
             ],
5568
             "properties": {
5567
             "properties": {
5569
                 "auto_register": {
5568
                 "auto_register": {
@@ -5593,9 +5592,6 @@
5593
                 "pkce_method": {
5592
                 "pkce_method": {
5594
                     "type": "string"
5593
                     "type": "string"
5595
                 },
5594
                 },
5596
-                "redirect_url": {
5597
-                    "type": "string"
5598
-                },
5599
                 "scopes": {
5595
                 "scopes": {
5600
                     "type": "string"
5596
                     "type": "string"
5601
                 }
5597
                 }
@@ -6289,9 +6285,6 @@
6289
                 "pkce_method": {
6285
                 "pkce_method": {
6290
                     "type": "string"
6286
                     "type": "string"
6291
                 },
6287
                 },
6292
-                "redirect_url": {
6293
-                    "type": "string"
6294
-                },
6295
                 "scopes": {
6288
                 "scopes": {
6296
                     "type": "string"
6289
                     "type": "string"
6297
                 },
6290
                 },
@@ -6595,4 +6588,4 @@
6595
             "in": "header"
6588
             "in": "header"
6596
         }
6589
         }
6597
     }
6590
     }
6598
-}
6591
+}

+ 0 - 5
docs/admin/admin_swagger.yaml

@@ -143,15 +143,12 @@ definitions:
143
         type: boolean
143
         type: boolean
144
       pkce_method:
144
       pkce_method:
145
         type: string
145
         type: string
146
-      redirect_url:
147
-        type: string
148
       scopes:
146
       scopes:
149
         type: string
147
         type: string
150
     required:
148
     required:
151
     - client_id
149
     - client_id
152
     - client_secret
150
     - client_secret
153
     - oauth_type
151
     - oauth_type
154
-    - redirect_url
155
     type: object
152
     type: object
156
   admin.PeerBatchDeleteForm:
153
   admin.PeerBatchDeleteForm:
157
     properties:
154
     properties:
@@ -611,8 +608,6 @@ definitions:
611
         type: boolean
608
         type: boolean
612
       pkce_method:
609
       pkce_method:
613
         type: string
610
         type: string
614
-      redirect_url:
615
-        type: string
616
       scopes:
611
       scopes:
617
         type: string
612
         type: string
618
       updated_at:
613
       updated_at:

+ 2 - 1
http/controller/admin/login.go

@@ -2,6 +2,7 @@ package admin
2
 
2
 
3
 import (
3
 import (
4
 	"fmt"
4
 	"fmt"
5
+
5
 	"github.com/gin-gonic/gin"
6
 	"github.com/gin-gonic/gin"
6
 	"github.com/lejianwen/rustdesk-api/v2/global"
7
 	"github.com/lejianwen/rustdesk-api/v2/global"
7
 	"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
8
 	"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
@@ -188,7 +189,7 @@ func (ct *Login) OidcAuth(c *gin.Context) {
188
 		return
189
 		return
189
 	}
190
 	}
190
 
191
 
191
-	err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
192
+	err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(c, f.Op)
192
 	if err != nil {
193
 	if err != nil {
193
 		response.Error(c, response.TranslateMsg(c, err.Error()))
194
 		response.Error(c, response.TranslateMsg(c, err.Error()))
194
 		return
195
 		return

+ 3 - 2
http/controller/admin/oauth.go

@@ -1,13 +1,14 @@
1
 package admin
1
 package admin
2
 
2
 
3
 import (
3
 import (
4
+	"strconv"
5
+
4
 	"github.com/gin-gonic/gin"
6
 	"github.com/gin-gonic/gin"
5
 	"github.com/lejianwen/rustdesk-api/v2/global"
7
 	"github.com/lejianwen/rustdesk-api/v2/global"
6
 	"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
8
 	"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
7
 	adminReq "github.com/lejianwen/rustdesk-api/v2/http/request/admin"
9
 	adminReq "github.com/lejianwen/rustdesk-api/v2/http/request/admin"
8
 	"github.com/lejianwen/rustdesk-api/v2/http/response"
10
 	"github.com/lejianwen/rustdesk-api/v2/http/response"
9
 	"github.com/lejianwen/rustdesk-api/v2/service"
11
 	"github.com/lejianwen/rustdesk-api/v2/service"
10
-	"strconv"
11
 )
12
 )
12
 
13
 
13
 type Oauth struct {
14
 type Oauth struct {
@@ -43,7 +44,7 @@ func (o *Oauth) ToBind(c *gin.Context) {
43
 		return
44
 		return
44
 	}
45
 	}
45
 
46
 
46
-	err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
47
+	err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(c, f.Op)
47
 	if err != nil {
48
 	if err != nil {
48
 		response.Error(c, response.TranslateMsg(c, err.Error()))
49
 		response.Error(c, response.TranslateMsg(c, err.Error()))
49
 		return
50
 		return

+ 6 - 7
http/controller/api/ouath.go

@@ -1,6 +1,8 @@
1
 package api
1
 package api
2
 
2
 
3
 import (
3
 import (
4
+	"net/http"
5
+
4
 	"github.com/gin-gonic/gin"
6
 	"github.com/gin-gonic/gin"
5
 	"github.com/lejianwen/rustdesk-api/v2/global"
7
 	"github.com/lejianwen/rustdesk-api/v2/global"
6
 	"github.com/lejianwen/rustdesk-api/v2/http/request/api"
8
 	"github.com/lejianwen/rustdesk-api/v2/http/request/api"
@@ -10,7 +12,6 @@ import (
10
 	"github.com/lejianwen/rustdesk-api/v2/service"
12
 	"github.com/lejianwen/rustdesk-api/v2/service"
11
 	"github.com/lejianwen/rustdesk-api/v2/utils"
13
 	"github.com/lejianwen/rustdesk-api/v2/utils"
12
 	"github.com/nicksnyder/go-i18n/v2/i18n"
14
 	"github.com/nicksnyder/go-i18n/v2/i18n"
13
-	"net/http"
14
 )
15
 )
15
 
16
 
16
 type Oauth struct {
17
 type Oauth struct {
@@ -35,7 +36,7 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
35
 
36
 
36
 	oauthService := service.AllService.OauthService
37
 	oauthService := service.AllService.OauthService
37
 
38
 
38
-	err, state, verifier, nonce, url := oauthService.BeginAuth(f.Op)
39
+	err, state, verifier, nonce, url := oauthService.BeginAuth(c, f.Op)
39
 	if err != nil {
40
 	if err != nil {
40
 		response.Error(c, response.TranslateMsg(c, err.Error()))
41
 		response.Error(c, response.TranslateMsg(c, err.Error()))
41
 		return
42
 		return
@@ -169,7 +170,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
169
 	var user *model.User
170
 	var user *model.User
170
 	// 获取用户信息
171
 	// 获取用户信息
171
 	code := c.Query("code")
172
 	code := c.Query("code")
172
-	err, oauthUser := oauthService.Callback(code, verifier, op, nonce)
173
+	err, oauthUser := oauthService.Callback(c, code, verifier, op, nonce)
173
 	if err != nil {
174
 	if err != nil {
174
 		c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
175
 		c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
175
 			"message":     "OauthFailed",
176
 			"message":     "OauthFailed",
@@ -225,8 +226,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
225
 			if !*oauthConfig.AutoRegister {
226
 			if !*oauthConfig.AutoRegister {
226
 				//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
227
 				//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
227
 				oauthCache.UpdateFromOauthUser(oauthUser)
228
 				oauthCache.UpdateFromOauthUser(oauthUser)
228
-				url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
229
-				c.Redirect(http.StatusFound, url)
229
+				c.Redirect(http.StatusFound, "/_admin/#/oauth/bind/"+cacheKey)
230
 				return
230
 				return
231
 			}
231
 			}
232
 
232
 
@@ -251,8 +251,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
251
 				Type:     model.LoginLogTypeOauth,
251
 				Type:     model.LoginLogTypeOauth,
252
 				Platform: oauthService.DeviceOs,
252
 				Platform: oauthService.DeviceOs,
253
 			})*/
253
 			})*/
254
-			url := global.Config.Rustdesk.ApiServer + "/_admin/#/"
255
-			c.Redirect(http.StatusFound, url)
254
+			c.Redirect(http.StatusFound, "/_admin/#/")
256
 			return
255
 			return
257
 		}
256
 		}
258
 		c.HTML(http.StatusOK, "oauth_success.html", gin.H{
257
 		c.HTML(http.StatusOK, "oauth_success.html", gin.H{

+ 0 - 2
http/request/admin/oauth.go

@@ -22,7 +22,6 @@ type OauthForm struct {
22
 	Scopes       string `json:"scopes" validate:"omitempty"`
22
 	Scopes       string `json:"scopes" validate:"omitempty"`
23
 	ClientId     string `json:"client_id" validate:"required"`
23
 	ClientId     string `json:"client_id" validate:"required"`
24
 	ClientSecret string `json:"client_secret" validate:"required"`
24
 	ClientSecret string `json:"client_secret" validate:"required"`
25
-	RedirectUrl  string `json:"redirect_url" validate:"required"`
26
 	AutoRegister *bool  `json:"auto_register"`
25
 	AutoRegister *bool  `json:"auto_register"`
27
 	PkceEnable   *bool  `json:"pkce_enable"`
26
 	PkceEnable   *bool  `json:"pkce_enable"`
28
 	PkceMethod   string `json:"pkce_method"`
27
 	PkceMethod   string `json:"pkce_method"`
@@ -34,7 +33,6 @@ func (of *OauthForm) ToOauth() *model.Oauth {
34
 		OauthType:    of.OauthType,
33
 		OauthType:    of.OauthType,
35
 		ClientId:     of.ClientId,
34
 		ClientId:     of.ClientId,
36
 		ClientSecret: of.ClientSecret,
35
 		ClientSecret: of.ClientSecret,
37
-		RedirectUrl:  of.RedirectUrl,
38
 		AutoRegister: of.AutoRegister,
36
 		AutoRegister: of.AutoRegister,
39
 		Issuer:       of.Issuer,
37
 		Issuer:       of.Issuer,
40
 		Scopes:       of.Scopes,
38
 		Scopes:       of.Scopes,

+ 4 - 5
model/oauth.go

@@ -30,9 +30,9 @@ func ValidateOauthType(oauthType string) error {
30
 }
30
 }
31
 
31
 
32
 const (
32
 const (
33
-	UserEndpointGithub string = "https://api.github.com/user"
33
+	UserEndpointGithub  string = "https://api.github.com/user"
34
 	UserEndpointLinuxdo string = "https://connect.linux.do/api/user"
34
 	UserEndpointLinuxdo string = "https://connect.linux.do/api/user"
35
-	IssuerGoogle       string = "https://accounts.google.com"
35
+	IssuerGoogle        string = "https://accounts.google.com"
36
 )
36
 )
37
 
37
 
38
 type Oauth struct {
38
 type Oauth struct {
@@ -41,12 +41,11 @@ type Oauth struct {
41
 	OauthType    string `json:"oauth_type"`
41
 	OauthType    string `json:"oauth_type"`
42
 	ClientId     string `json:"client_id"`
42
 	ClientId     string `json:"client_id"`
43
 	ClientSecret string `json:"client_secret"`
43
 	ClientSecret string `json:"client_secret"`
44
-	RedirectUrl  string `json:"redirect_url"`
45
 	AutoRegister *bool  `json:"auto_register"`
44
 	AutoRegister *bool  `json:"auto_register"`
46
 	Scopes       string `json:"scopes"`
45
 	Scopes       string `json:"scopes"`
47
 	Issuer       string `json:"issuer"`
46
 	Issuer       string `json:"issuer"`
48
-	PkceEnable	 *bool  `json:"pkce_enable"`
49
-	PkceMethod	 string `json:"pkce_method"`
47
+	PkceEnable   *bool  `json:"pkce_enable"`
48
+	PkceMethod   string `json:"pkce_method"`
50
 	TimeModel
49
 	TimeModel
51
 }
50
 }
52
 
51
 

+ 17 - 10
service/oauth.go

@@ -4,11 +4,14 @@ import (
4
 	"context"
4
 	"context"
5
 	"encoding/json"
5
 	"encoding/json"
6
 	"errors"
6
 	"errors"
7
+
7
 	"github.com/coreos/go-oidc/v3/oidc"
8
 	"github.com/coreos/go-oidc/v3/oidc"
9
+	"github.com/gin-gonic/gin"
8
 	"github.com/lejianwen/rustdesk-api/v2/model"
10
 	"github.com/lejianwen/rustdesk-api/v2/model"
9
 	"github.com/lejianwen/rustdesk-api/v2/utils"
11
 	"github.com/lejianwen/rustdesk-api/v2/utils"
10
 	"golang.org/x/oauth2"
12
 	"golang.org/x/oauth2"
11
 	"golang.org/x/oauth2/github"
13
 	"golang.org/x/oauth2/github"
14
+
12
 	// "golang.org/x/oauth2/google"
15
 	// "golang.org/x/oauth2/google"
13
 	"gorm.io/gorm"
16
 	"gorm.io/gorm"
14
 	// "io"
17
 	// "io"
@@ -93,16 +96,20 @@ func (os *OauthService) DeleteOauthCache(key string) {
93
 	OauthCache.Delete(key)
96
 	OauthCache.Delete(key)
94
 }
97
 }
95
 
98
 
96
-func (os *OauthService) BeginAuth(op string) (error error, state, verifier, nonce, url string) {
99
+func (os *OauthService) BeginAuth(c *gin.Context, op string) (error error, state, verifier, nonce, url string) {
97
 	state = utils.RandomString(10) + strconv.FormatInt(time.Now().Unix(), 10)
100
 	state = utils.RandomString(10) + strconv.FormatInt(time.Now().Unix(), 10)
98
 	verifier = ""
101
 	verifier = ""
99
 	nonce = ""
102
 	nonce = ""
100
 	if op == model.OauthTypeWebauth {
103
 	if op == model.OauthTypeWebauth {
101
-		url = Config.Rustdesk.ApiServer + "/_admin/#/oauth/" + state
104
+		host := c.GetHeader("Origin")
105
+		if host == "" {
106
+			host = Config.Rustdesk.ApiServer
107
+		}
108
+		url = host + "/_admin/#/oauth/" + state
102
 		//url = "http://localhost:8888/_admin/#/oauth/" + code
109
 		//url = "http://localhost:8888/_admin/#/oauth/" + code
103
 		return nil, state, verifier, nonce, url
110
 		return nil, state, verifier, nonce, url
104
 	}
111
 	}
105
-	err, oauthInfo, oauthConfig, _ := os.GetOauthConfig(op)
112
+	err, oauthInfo, oauthConfig, _ := os.GetOauthConfig(c, op)
106
 	if err == nil {
113
 	if err == nil {
107
 		extras := make([]oauth2.AuthCodeOption, 0, 3)
114
 		extras := make([]oauth2.AuthCodeOption, 0, 3)
108
 
115
 
@@ -167,20 +174,20 @@ func (os *OauthService) LinuxdoProvider() *oidc.Provider {
167
 }
174
 }
168
 
175
 
169
 // GetOauthConfig retrieves the OAuth2 configuration based on the provider name
176
 // GetOauthConfig retrieves the OAuth2 configuration based on the provider name
170
-func (os *OauthService) GetOauthConfig(op string) (err error, oauthInfo *model.Oauth, oauthConfig *oauth2.Config, provider *oidc.Provider) {
177
+func (os *OauthService) GetOauthConfig(c *gin.Context, op string) (err error, oauthInfo *model.Oauth, oauthConfig *oauth2.Config, provider *oidc.Provider) {
171
 	//err, oauthInfo, oauthConfig = os.getOauthConfigGeneral(op)
178
 	//err, oauthInfo, oauthConfig = os.getOauthConfigGeneral(op)
172
 	oauthInfo = os.InfoByOp(op)
179
 	oauthInfo = os.InfoByOp(op)
173
 	if oauthInfo.Id == 0 || oauthInfo.ClientId == "" || oauthInfo.ClientSecret == "" {
180
 	if oauthInfo.Id == 0 || oauthInfo.ClientId == "" || oauthInfo.ClientSecret == "" {
174
 		return errors.New("ConfigNotFound"), nil, nil, nil
181
 		return errors.New("ConfigNotFound"), nil, nil, nil
175
 	}
182
 	}
176
-	// If the redirect URL is empty, use the default redirect URL
177
-	if oauthInfo.RedirectUrl == "" {
178
-		oauthInfo.RedirectUrl = Config.Rustdesk.ApiServer + "/api/oidc/callback"
183
+	host := c.GetHeader("Origin")
184
+	if host == "" {
185
+		host = Config.Rustdesk.ApiServer
179
 	}
186
 	}
180
 	oauthConfig = &oauth2.Config{
187
 	oauthConfig = &oauth2.Config{
181
 		ClientID:     oauthInfo.ClientId,
188
 		ClientID:     oauthInfo.ClientId,
182
 		ClientSecret: oauthInfo.ClientSecret,
189
 		ClientSecret: oauthInfo.ClientSecret,
183
-		RedirectURL:  oauthInfo.RedirectUrl,
190
+		RedirectURL:  host + "/api/oidc/callback",
184
 	}
191
 	}
185
 
192
 
186
 	// Maybe should validate the oauthConfig here
193
 	// Maybe should validate the oauthConfig here
@@ -335,8 +342,8 @@ func (os *OauthService) oidcCallback(oauthConfig *oauth2.Config, provider *oidc.
335
 }
342
 }
336
 
343
 
337
 // Callback: Get user information by code and op(Oauth provider)
344
 // Callback: Get user information by code and op(Oauth provider)
338
-func (os *OauthService) Callback(code, verifier, op, nonce string) (err error, oauthUser *model.OauthUser) {
339
-	err, oauthInfo, oauthConfig, provider := os.GetOauthConfig(op)
345
+func (os *OauthService) Callback(c *gin.Context, code, verifier, op, nonce string) (err error, oauthUser *model.OauthUser) {
346
+	err, oauthInfo, oauthConfig, provider := os.GetOauthConfig(c, op)
340
 	// oauthType is already validated in GetOauthConfig
347
 	// oauthType is already validated in GetOauthConfig
341
 	if err != nil {
348
 	if err != nil {
342
 		return err, nil
349
 		return err, nil