oauth.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package model
  2. import (
  3. "errors"
  4. "strconv"
  5. "strings"
  6. )
  7. const OIDC_DEFAULT_SCOPES = "openid,profile,email"
  8. const (
  9. // make sure the value shouldbe lowercase
  10. OauthTypeGithub string = "github"
  11. OauthTypeGoogle string = "google"
  12. OauthTypeOidc string = "oidc"
  13. OauthTypeWebauth string = "webauth"
  14. OauthTypeLinuxdo string = "linuxdo"
  15. PKCEMethodS256 string = "S256"
  16. PKCEMethodPlain string = "plain"
  17. )
  18. // Validate the oauth type
  19. func ValidateOauthType(oauthType string) error {
  20. switch oauthType {
  21. case OauthTypeGithub, OauthTypeGoogle, OauthTypeOidc, OauthTypeWebauth, OauthTypeLinuxdo:
  22. return nil
  23. default:
  24. return errors.New("invalid Oauth type")
  25. }
  26. }
  27. const (
  28. UserEndpointGithub string = "https://api.github.com/user"
  29. UserEndpointLinuxdo string = "https://connect.linux.do/api/user"
  30. IssuerGoogle string = "https://accounts.google.com"
  31. )
  32. type Oauth struct {
  33. IdModel
  34. Op string `json:"op"`
  35. OauthType string `json:"oauth_type"`
  36. ClientId string `json:"client_id"`
  37. ClientSecret string `json:"client_secret"`
  38. RedirectUrl string `json:"redirect_url"`
  39. AutoRegister *bool `json:"auto_register"`
  40. Scopes string `json:"scopes"`
  41. Issuer string `json:"issuer"`
  42. PkceEnable *bool `json:"pkce_enable"`
  43. PkceMethod string `json:"pkce_method"`
  44. TimeModel
  45. }
  46. // Helper function to format oauth info, it's used in the update and create method
  47. func (oa *Oauth) FormatOauthInfo() error {
  48. oauthType := strings.TrimSpace(oa.OauthType)
  49. err := ValidateOauthType(oa.OauthType)
  50. if err != nil {
  51. return err
  52. }
  53. switch oauthType {
  54. case OauthTypeGithub:
  55. oa.Op = OauthTypeGithub
  56. case OauthTypeGoogle:
  57. oa.Op = OauthTypeGoogle
  58. case OauthTypeLinuxdo:
  59. oa.Op = OauthTypeLinuxdo
  60. }
  61. // check if the op is empty, set the default value
  62. op := strings.TrimSpace(oa.Op)
  63. if op == "" && oauthType == OauthTypeOidc {
  64. oa.Op = OauthTypeOidc
  65. }
  66. // check the issuer, if the oauth type is google and the issuer is empty, set the issuer to the default value
  67. issuer := strings.TrimSpace(oa.Issuer)
  68. // If the oauth type is google and the issuer is empty, set the issuer to the default value
  69. if oauthType == OauthTypeGoogle && issuer == "" {
  70. oa.Issuer = IssuerGoogle
  71. }
  72. if oa.PkceEnable == nil {
  73. oa.PkceEnable = new(bool)
  74. *oa.PkceEnable = false
  75. }
  76. if oa.PkceMethod == "" {
  77. oa.PkceMethod = PKCEMethodS256
  78. }
  79. return nil
  80. }
  81. type OauthUser struct {
  82. OpenId string `json:"open_id" gorm:"not null;index"`
  83. Name string `json:"name"`
  84. Username string `json:"username"`
  85. Email string `json:"email"`
  86. VerifiedEmail bool `json:"verified_email,omitempty"`
  87. Picture string `json:"picture,omitempty"`
  88. }
  89. func (ou *OauthUser) ToUser(user *User, overideUsername bool) {
  90. if overideUsername {
  91. user.Username = ou.Username
  92. }
  93. user.Email = ou.Email
  94. user.Nickname = ou.Name
  95. user.Avatar = ou.Picture
  96. }
  97. type OauthUserBase struct {
  98. Name string `json:"name"`
  99. Email string `json:"email"`
  100. }
  101. type OidcUser struct {
  102. OauthUserBase
  103. Sub string `json:"sub"`
  104. VerifiedEmail bool `json:"email_verified"`
  105. PreferredUsername string `json:"preferred_username"`
  106. Picture string `json:"picture"`
  107. }
  108. func (ou *OidcUser) ToOauthUser() *OauthUser {
  109. var username string
  110. // 使用 PreferredUsername,如果不存在,降级到 Email 前缀
  111. if ou.PreferredUsername != "" {
  112. username = ou.PreferredUsername
  113. } else {
  114. username = strings.ToLower(ou.Email)
  115. }
  116. return &OauthUser{
  117. OpenId: ou.Sub,
  118. Name: ou.Name,
  119. Username: username,
  120. Email: ou.Email,
  121. VerifiedEmail: ou.VerifiedEmail,
  122. Picture: ou.Picture,
  123. }
  124. }
  125. type GithubUser struct {
  126. OauthUserBase
  127. Id int `json:"id"`
  128. Login string `json:"login"`
  129. AvatarUrl string `json:"avatar_url"`
  130. VerifiedEmail bool `json:"verified_email"`
  131. }
  132. func (gu *GithubUser) ToOauthUser() *OauthUser {
  133. username := strings.ToLower(gu.Login)
  134. return &OauthUser{
  135. OpenId: strconv.Itoa(gu.Id),
  136. Name: gu.Name,
  137. Username: username,
  138. Email: gu.Email,
  139. VerifiedEmail: gu.VerifiedEmail,
  140. Picture: gu.AvatarUrl,
  141. }
  142. }
  143. type LinuxdoUser struct {
  144. OauthUserBase
  145. Id int `json:"id"`
  146. Username string `json:"username"`
  147. Avatar string `json:"avatar_url"`
  148. }
  149. func (lu *LinuxdoUser) ToOauthUser() *OauthUser {
  150. return &OauthUser{
  151. OpenId: strconv.Itoa(lu.Id),
  152. Name: lu.Name,
  153. Username: strings.ToLower(lu.Username),
  154. Email: lu.Email,
  155. VerifiedEmail: true, // linux.do 用户邮箱默认已验证
  156. Picture: lu.Avatar,
  157. }
  158. }
  159. type OauthList struct {
  160. Oauths []*Oauth `json:"list"`
  161. Pagination
  162. }