Просмотр исходного кода

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
Родитель
Сommit
73a8461a2d

+ 5 - 4
cmd/apimain.go

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

+ 0 - 4
config/oauth.go

@@ -3,24 +3,20 @@ package config
3 3
 type GithubOauth struct {
4 4
 	ClientId     string `mapstructure:"client-id"`
5 5
 	ClientSecret string `mapstructure:"client-secret"`
6
-	RedirectUrl  string `mapstructure:"redirect-url"`
7 6
 }
8 7
 
9 8
 type GoogleOauth struct {
10 9
 	ClientId     string `mapstructure:"client-id"`
11 10
 	ClientSecret string `mapstructure:"client-secret"`
12
-	RedirectUrl  string `mapstructure:"redirect-url"`
13 11
 }
14 12
 
15 13
 type OidcOauth struct {
16 14
 	Issuer       string `mapstructure:"issuer"`
17 15
 	ClientId     string `mapstructure:"client-id"`
18 16
 	ClientSecret string `mapstructure:"client-secret"`
19
-	RedirectUrl  string `mapstructure:"redirect-url"`
20 17
 }
21 18
 
22 19
 type LinuxdoOauth struct {
23 20
 	ClientId     string `mapstructure:"client-id"`
24 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 5569
             "required": [
5570 5570
                 "client_id",
5571 5571
                 "client_secret",
5572
-                "oauth_type",
5573
-                "redirect_url"
5572
+                "oauth_type"
5574 5573
             ],
5575 5574
             "properties": {
5576 5575
                 "auto_register": {
@@ -5600,9 +5599,6 @@ const docTemplateadmin = `{
5600 5599
                 "pkce_method": {
5601 5600
                     "type": "string"
5602 5601
                 },
5603
-                "redirect_url": {
5604
-                    "type": "string"
5605
-                },
5606 5602
                 "scopes": {
5607 5603
                     "type": "string"
5608 5604
                 }
@@ -6296,9 +6292,6 @@ const docTemplateadmin = `{
6296 6292
                 "pkce_method": {
6297 6293
                     "type": "string"
6298 6294
                 },
6299
-                "redirect_url": {
6300
-                    "type": "string"
6301
-                },
6302 6295
                 "scopes": {
6303 6296
                     "type": "string"
6304 6297
                 },

+ 2 - 9
docs/admin/admin_swagger.json

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

+ 0 - 5
docs/admin/admin_swagger.yaml

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

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

@@ -2,6 +2,7 @@ package admin
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+
5 6
 	"github.com/gin-gonic/gin"
6 7
 	"github.com/lejianwen/rustdesk-api/v2/global"
7 8
 	"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
@@ -188,7 +189,7 @@ func (ct *Login) OidcAuth(c *gin.Context) {
188 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 193
 	if err != nil {
193 194
 		response.Error(c, response.TranslateMsg(c, err.Error()))
194 195
 		return

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

@@ -1,13 +1,14 @@
1 1
 package admin
2 2
 
3 3
 import (
4
+	"strconv"
5
+
4 6
 	"github.com/gin-gonic/gin"
5 7
 	"github.com/lejianwen/rustdesk-api/v2/global"
6 8
 	"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
7 9
 	adminReq "github.com/lejianwen/rustdesk-api/v2/http/request/admin"
8 10
 	"github.com/lejianwen/rustdesk-api/v2/http/response"
9 11
 	"github.com/lejianwen/rustdesk-api/v2/service"
10
-	"strconv"
11 12
 )
12 13
 
13 14
 type Oauth struct {
@@ -43,7 +44,7 @@ func (o *Oauth) ToBind(c *gin.Context) {
43 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 48
 	if err != nil {
48 49
 		response.Error(c, response.TranslateMsg(c, err.Error()))
49 50
 		return

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

@@ -1,6 +1,8 @@
1 1
 package api
2 2
 
3 3
 import (
4
+	"net/http"
5
+
4 6
 	"github.com/gin-gonic/gin"
5 7
 	"github.com/lejianwen/rustdesk-api/v2/global"
6 8
 	"github.com/lejianwen/rustdesk-api/v2/http/request/api"
@@ -10,7 +12,6 @@ import (
10 12
 	"github.com/lejianwen/rustdesk-api/v2/service"
11 13
 	"github.com/lejianwen/rustdesk-api/v2/utils"
12 14
 	"github.com/nicksnyder/go-i18n/v2/i18n"
13
-	"net/http"
14 15
 )
15 16
 
16 17
 type Oauth struct {
@@ -35,7 +36,7 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
35 36
 
36 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 40
 	if err != nil {
40 41
 		response.Error(c, response.TranslateMsg(c, err.Error()))
41 42
 		return
@@ -169,7 +170,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
169 170
 	var user *model.User
170 171
 	// 获取用户信息
171 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 174
 	if err != nil {
174 175
 		c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
175 176
 			"message":     "OauthFailed",
@@ -225,8 +226,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
225 226
 			if !*oauthConfig.AutoRegister {
226 227
 				//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
227 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 230
 				return
231 231
 			}
232 232
 
@@ -251,8 +251,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
251 251
 				Type:     model.LoginLogTypeOauth,
252 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 255
 			return
257 256
 		}
258 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 22
 	Scopes       string `json:"scopes" validate:"omitempty"`
23 23
 	ClientId     string `json:"client_id" validate:"required"`
24 24
 	ClientSecret string `json:"client_secret" validate:"required"`
25
-	RedirectUrl  string `json:"redirect_url" validate:"required"`
26 25
 	AutoRegister *bool  `json:"auto_register"`
27 26
 	PkceEnable   *bool  `json:"pkce_enable"`
28 27
 	PkceMethod   string `json:"pkce_method"`
@@ -34,7 +33,6 @@ func (of *OauthForm) ToOauth() *model.Oauth {
34 33
 		OauthType:    of.OauthType,
35 34
 		ClientId:     of.ClientId,
36 35
 		ClientSecret: of.ClientSecret,
37
-		RedirectUrl:  of.RedirectUrl,
38 36
 		AutoRegister: of.AutoRegister,
39 37
 		Issuer:       of.Issuer,
40 38
 		Scopes:       of.Scopes,

+ 4 - 5
model/oauth.go

@@ -30,9 +30,9 @@ func ValidateOauthType(oauthType string) error {
30 30
 }
31 31
 
32 32
 const (
33
-	UserEndpointGithub string = "https://api.github.com/user"
33
+	UserEndpointGithub  string = "https://api.github.com/user"
34 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 38
 type Oauth struct {
@@ -41,12 +41,11 @@ type Oauth struct {
41 41
 	OauthType    string `json:"oauth_type"`
42 42
 	ClientId     string `json:"client_id"`
43 43
 	ClientSecret string `json:"client_secret"`
44
-	RedirectUrl  string `json:"redirect_url"`
45 44
 	AutoRegister *bool  `json:"auto_register"`
46 45
 	Scopes       string `json:"scopes"`
47 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 49
 	TimeModel
51 50
 }
52 51
 

+ 17 - 10
service/oauth.go

@@ -4,11 +4,14 @@ import (
4 4
 	"context"
5 5
 	"encoding/json"
6 6
 	"errors"
7
+
7 8
 	"github.com/coreos/go-oidc/v3/oidc"
9
+	"github.com/gin-gonic/gin"
8 10
 	"github.com/lejianwen/rustdesk-api/v2/model"
9 11
 	"github.com/lejianwen/rustdesk-api/v2/utils"
10 12
 	"golang.org/x/oauth2"
11 13
 	"golang.org/x/oauth2/github"
14
+
12 15
 	// "golang.org/x/oauth2/google"
13 16
 	"gorm.io/gorm"
14 17
 	// "io"
@@ -93,16 +96,20 @@ func (os *OauthService) DeleteOauthCache(key string) {
93 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 100
 	state = utils.RandomString(10) + strconv.FormatInt(time.Now().Unix(), 10)
98 101
 	verifier = ""
99 102
 	nonce = ""
100 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 109
 		//url = "http://localhost:8888/_admin/#/oauth/" + code
103 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 113
 	if err == nil {
107 114
 		extras := make([]oauth2.AuthCodeOption, 0, 3)
108 115
 
@@ -167,20 +174,20 @@ func (os *OauthService) LinuxdoProvider() *oidc.Provider {
167 174
 }
168 175
 
169 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 178
 	//err, oauthInfo, oauthConfig = os.getOauthConfigGeneral(op)
172 179
 	oauthInfo = os.InfoByOp(op)
173 180
 	if oauthInfo.Id == 0 || oauthInfo.ClientId == "" || oauthInfo.ClientSecret == "" {
174 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 187
 	oauthConfig = &oauth2.Config{
181 188
 		ClientID:     oauthInfo.ClientId,
182 189
 		ClientSecret: oauthInfo.ClientSecret,
183
-		RedirectURL:  oauthInfo.RedirectUrl,
190
+		RedirectURL:  host + "/api/oidc/callback",
184 191
 	}
185 192
 
186 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 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 347
 	// oauthType is already validated in GetOauthConfig
341 348
 	if err != nil {
342 349
 		return err, nil