social.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package user
  5. import (
  6. "encoding/json"
  7. "net/http"
  8. "net/url"
  9. "strconv"
  10. "strings"
  11. "code.google.com/p/goauth2/oauth"
  12. "github.com/gogits/gogs/models"
  13. "github.com/gogits/gogs/modules/base"
  14. "github.com/gogits/gogs/modules/log"
  15. "github.com/gogits/gogs/modules/middleware"
  16. "github.com/gogits/gogs/modules/oauth2"
  17. )
  18. type SocialConnector interface {
  19. Identity() string
  20. Name() string
  21. Email() string
  22. TokenString() string
  23. }
  24. type SocialGithub struct {
  25. data struct {
  26. Id int `json:"id"`
  27. Name string `json:"login"`
  28. Email string `json:"email"`
  29. }
  30. Token *oauth.Token
  31. }
  32. func (s *SocialGithub) Identity() string {
  33. return strconv.Itoa(s.data.Id)
  34. }
  35. func (s *SocialGithub) Name() string {
  36. return s.data.Name
  37. }
  38. func (s *SocialGithub) Email() string {
  39. return s.data.Email
  40. }
  41. func (s *SocialGithub) TokenString() string {
  42. data, _ := json.Marshal(s.Token)
  43. return string(data)
  44. }
  45. // Github API refer: https://developer.github.com/v3/users/
  46. func (s *SocialGithub) Update() error {
  47. scope := "https://api.github.com/user"
  48. transport := &oauth.Transport{
  49. Token: s.Token,
  50. }
  51. log.Debug("update github info")
  52. r, err := transport.Client().Get(scope)
  53. if err != nil {
  54. return err
  55. }
  56. defer r.Body.Close()
  57. return json.NewDecoder(r.Body).Decode(&s.data)
  58. }
  59. func extractPath(next string) string {
  60. n, err := url.Parse(next)
  61. if err != nil {
  62. return "/"
  63. }
  64. return n.Path
  65. }
  66. // github && google && ...
  67. func SocialSignIn(ctx *middleware.Context, tokens oauth2.Tokens) {
  68. var socid int64
  69. var ok bool
  70. next := extractPath(ctx.Query("next"))
  71. log.Debug("social signed check %s", next)
  72. if socid, ok = ctx.Session.Get("socialId").(int64); ok && socid != 0 {
  73. // already login
  74. ctx.Redirect(next)
  75. log.Info("login soc id: %v", socid)
  76. return
  77. }
  78. config := &oauth.Config{
  79. ClientId: base.OauthService.GitHub.ClientId,
  80. ClientSecret: base.OauthService.GitHub.ClientSecret,
  81. RedirectURL: strings.TrimSuffix(base.AppUrl, "/") + ctx.Req.URL.RequestURI(),
  82. Scope: base.OauthService.GitHub.Scopes,
  83. AuthURL: "https://github.com/login/oauth/authorize",
  84. TokenURL: "https://github.com/login/oauth/access_token",
  85. }
  86. transport := &oauth.Transport{
  87. Config: config,
  88. Transport: http.DefaultTransport,
  89. }
  90. code := ctx.Query("code")
  91. if code == "" {
  92. // redirect to social login page
  93. ctx.Redirect(config.AuthCodeURL(next))
  94. return
  95. }
  96. // handle call back
  97. tk, err := transport.Exchange(code)
  98. if err != nil {
  99. log.Error("oauth2 handle callback error: %v", err)
  100. return // FIXME, need error page 501
  101. }
  102. next = extractPath(ctx.Query("state"))
  103. log.Debug("success token: %v", tk)
  104. gh := &SocialGithub{Token: tk}
  105. if err = gh.Update(); err != nil {
  106. // FIXME: handle error page 501
  107. log.Error("connect with github error: %s", err)
  108. return
  109. }
  110. var soc SocialConnector = gh
  111. log.Info("login: %s", soc.Name())
  112. oa, err := models.GetOauth2(soc.Identity())
  113. switch err {
  114. case nil:
  115. ctx.Session.Set("userId", oa.User.Id)
  116. ctx.Session.Set("userName", oa.User.Name)
  117. case models.ErrOauth2RecordNotExists:
  118. oa = &models.Oauth2{}
  119. oa.Uid = -1
  120. oa.Type = models.OT_GITHUB
  121. oa.Token = soc.TokenString()
  122. oa.Identity = soc.Identity()
  123. log.Debug("oa: %v", oa)
  124. if err = models.AddOauth2(oa); err != nil {
  125. log.Error("add oauth2 %v", err) // 501
  126. return
  127. }
  128. case models.ErrOauth2NotAssociatedWithUser:
  129. ctx.Session.Set("socialName", soc.Name())
  130. ctx.Session.Set("socialEmail", soc.Email())
  131. ctx.Session.Set("socialId", oa.Id)
  132. ctx.Redirect("/user/sign_up")
  133. return
  134. default:
  135. log.Error(err.Error()) // FIXME: handle error page
  136. return
  137. }
  138. ctx.Session.Set("socialId", oa.Id)
  139. log.Debug("socialId: %v", oa.Id)
  140. ctx.Redirect(next)
  141. }