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

feat(oauth): 支持linux.do登录 (#280)

* 支持linux.do登录

* 修正
puyujian месяцев назад: 7
Родитель
Сommit
bf39a29e6c
4 измененных файлов с 58 добавлено и 1 удалено
  1. 1 0
      .github/workflows/build.yml
  2. 6 0
      config/oauth.go
  3. 23 1
      model/oauth.go
  4. 28 0
      service/oauth.go

+ 1 - 0
.github/workflows/build.yml

@@ -147,6 +147,7 @@ jobs:
147 147
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
148 148
 
149 149
       - name: Generate Changelog
150
+        if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
150 151
         run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
151 152
         env:
152 153
           GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

+ 6 - 0
config/oauth.go

@@ -18,3 +18,9 @@ type OidcOauth struct {
18 18
 	ClientSecret string `mapstructure:"client-secret"`
19 19
 	RedirectUrl  string `mapstructure:"redirect-url"`
20 20
 }
21
+
22
+type LinuxdoOauth struct {
23
+	ClientId     string `mapstructure:"client-id"`
24
+	ClientSecret string `mapstructure:"client-secret"`
25
+	RedirectUrl  string `mapstructure:"redirect-url"`
26
+}

+ 23 - 1
model/oauth.go

@@ -14,6 +14,7 @@ const (
14 14
 	OauthTypeGoogle  string = "google"
15 15
 	OauthTypeOidc    string = "oidc"
16 16
 	OauthTypeWebauth string = "webauth"
17
+	OauthTypeLinuxdo string = "linuxdo"
17 18
 	PKCEMethodS256   string = "S256"
18 19
 	PKCEMethodPlain  string = "plain"
19 20
 )
@@ -21,7 +22,7 @@ const (
21 22
 // Validate the oauth type
22 23
 func ValidateOauthType(oauthType string) error {
23 24
 	switch oauthType {
24
-	case OauthTypeGithub, OauthTypeGoogle, OauthTypeOidc, OauthTypeWebauth:
25
+	case OauthTypeGithub, OauthTypeGoogle, OauthTypeOidc, OauthTypeWebauth, OauthTypeLinuxdo:
25 26
 		return nil
26 27
 	default:
27 28
 		return errors.New("invalid Oauth type")
@@ -30,6 +31,7 @@ func ValidateOauthType(oauthType string) error {
30 31
 
31 32
 const (
32 33
 	UserEndpointGithub string = "https://api.github.com/user"
34
+	UserEndpointLinuxdo string = "https://connect.linux.do/api/user"
33 35
 	IssuerGoogle       string = "https://accounts.google.com"
34 36
 )
35 37
 
@@ -60,6 +62,8 @@ func (oa *Oauth) FormatOauthInfo() error {
60 62
 		oa.Op = OauthTypeGithub
61 63
 	case OauthTypeGoogle:
62 64
 		oa.Op = OauthTypeGoogle
65
+	case OauthTypeLinuxdo:
66
+		oa.Op = OauthTypeLinuxdo
63 67
 	}
64 68
 	// check if the op is empty, set the default value
65 69
 	op := strings.TrimSpace(oa.Op)
@@ -152,6 +156,24 @@ func (gu *GithubUser) ToOauthUser() *OauthUser {
152 156
 	}
153 157
 }
154 158
 
159
+type LinuxdoUser struct {
160
+	OauthUserBase
161
+	Id       int    `json:"id"`
162
+	Username string `json:"username"`
163
+	Avatar   string `json:"avatar_url"`
164
+}
165
+
166
+func (lu *LinuxdoUser) ToOauthUser() *OauthUser {
167
+	return &OauthUser{
168
+		OpenId:        strconv.Itoa(lu.Id),
169
+		Name:          lu.Name,
170
+		Username:      strings.ToLower(lu.Username),
171
+		Email:         lu.Email,
172
+		VerifiedEmail: true, // linux.do 用户邮箱默认已验证
173
+		Picture:       lu.Avatar,
174
+	}
175
+}
176
+
155 177
 type OauthList struct {
156 178
 	Oauths []*Oauth `json:"list"`
157 179
 	Pagination

+ 28 - 0
service/oauth.go

@@ -154,6 +154,18 @@ func (os *OauthService) GithubProvider() *oidc.Provider {
154 154
 	}).NewProvider(context.Background())
155 155
 }
156 156
 
157
+func (os *OauthService) LinuxdoProvider() *oidc.Provider {
158
+	return (&oidc.ProviderConfig{
159
+		IssuerURL:     "",
160
+		AuthURL:       "https://connect.linux.do/oauth2/authorize",
161
+		TokenURL:      "https://connect.linux.do/oauth2/token",
162
+		DeviceAuthURL: "",
163
+		UserInfoURL:   model.UserEndpointLinuxdo,
164
+		JWKSURL:       "",
165
+		Algorithms:    nil,
166
+	}).NewProvider(context.Background())
167
+}
168
+
157 169
 // GetOauthConfig retrieves the OAuth2 configuration based on the provider name
158 170
 func (os *OauthService) GetOauthConfig(op string) (err error, oauthInfo *model.Oauth, oauthConfig *oauth2.Config, provider *oidc.Provider) {
159 171
 	//err, oauthInfo, oauthConfig = os.getOauthConfigGeneral(op)
@@ -182,6 +194,10 @@ func (os *OauthService) GetOauthConfig(op string) (err error, oauthInfo *model.O
182 194
 		oauthConfig.Endpoint = github.Endpoint
183 195
 		oauthConfig.Scopes = []string{"read:user", "user:email"}
184 196
 		provider = os.GithubProvider()
197
+	case model.OauthTypeLinuxdo:
198
+		provider = os.LinuxdoProvider()
199
+		oauthConfig.Endpoint = provider.Endpoint()
200
+		oauthConfig.Scopes = []string{"profile"}
185 201
 	//case model.OauthTypeGoogle: //google单独出来,可以少一次FetchOidcEndpoint请求
186 202
 	//	oauthConfig.Endpoint = google.Endpoint
187 203
 	//	oauthConfig.Scopes = os.constructScopes(oauthInfo.Scopes)
@@ -299,6 +315,16 @@ func (os *OauthService) githubCallback(oauthConfig *oauth2.Config, provider *oid
299 315
 	return nil, user.ToOauthUser()
300 316
 }
301 317
 
318
+// linuxdoCallback linux.do回调
319
+func (os *OauthService) linuxdoCallback(oauthConfig *oauth2.Config, provider *oidc.Provider, code, verifier, nonce string) (error, *model.OauthUser) {
320
+	var user = &model.LinuxdoUser{}
321
+	err, _ := os.callbackBase(oauthConfig, provider, code, verifier, nonce, user)
322
+	if err != nil {
323
+		return err, nil
324
+	}
325
+	return nil, user.ToOauthUser()
326
+}
327
+
302 328
 // oidcCallback oidc回调, 通过code获取用户信息
303 329
 func (os *OauthService) oidcCallback(oauthConfig *oauth2.Config, provider *oidc.Provider, code, verifier, nonce string) (error, *model.OauthUser) {
304 330
 	var user = &model.OidcUser{}
@@ -319,6 +345,8 @@ func (os *OauthService) Callback(code, verifier, op, nonce string) (err error, o
319 345
 	switch oauthType {
320 346
 	case model.OauthTypeGithub:
321 347
 		err, oauthUser = os.githubCallback(oauthConfig, provider, code, verifier, nonce)
348
+	case model.OauthTypeLinuxdo:
349
+		err, oauthUser = os.linuxdoCallback(oauthConfig, provider, code, verifier, nonce)
322 350
 	case model.OauthTypeOidc, model.OauthTypeGoogle:
323 351
 		err, oauthUser = os.oidcCallback(oauthConfig, provider, code, verifier, nonce)
324 352
 	default: