You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package auth
  4. import (
  5. go_context "context"
  6. "encoding/base64"
  7. "errors"
  8. "fmt"
  9. "html"
  10. "io"
  11. "net/http"
  12. "net/url"
  13. "sort"
  14. "strings"
  15. "code.gitea.io/gitea/models/auth"
  16. org_model "code.gitea.io/gitea/models/organization"
  17. user_model "code.gitea.io/gitea/models/user"
  18. auth_module "code.gitea.io/gitea/modules/auth"
  19. "code.gitea.io/gitea/modules/base"
  20. "code.gitea.io/gitea/modules/container"
  21. "code.gitea.io/gitea/modules/context"
  22. "code.gitea.io/gitea/modules/json"
  23. "code.gitea.io/gitea/modules/log"
  24. "code.gitea.io/gitea/modules/setting"
  25. "code.gitea.io/gitea/modules/timeutil"
  26. "code.gitea.io/gitea/modules/util"
  27. "code.gitea.io/gitea/modules/web"
  28. "code.gitea.io/gitea/modules/web/middleware"
  29. auth_service "code.gitea.io/gitea/services/auth"
  30. source_service "code.gitea.io/gitea/services/auth/source"
  31. "code.gitea.io/gitea/services/auth/source/oauth2"
  32. "code.gitea.io/gitea/services/externalaccount"
  33. "code.gitea.io/gitea/services/forms"
  34. user_service "code.gitea.io/gitea/services/user"
  35. "gitea.com/go-chi/binding"
  36. "github.com/golang-jwt/jwt/v5"
  37. "github.com/markbates/goth"
  38. "github.com/markbates/goth/gothic"
  39. go_oauth2 "golang.org/x/oauth2"
  40. )
  41. const (
  42. tplGrantAccess base.TplName = "user/auth/grant"
  43. tplGrantError base.TplName = "user/auth/grant_error"
  44. )
  45. // TODO move error and responses to SDK or models
  46. // AuthorizeErrorCode represents an error code specified in RFC 6749
  47. // https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2.1
  48. type AuthorizeErrorCode string
  49. const (
  50. // ErrorCodeInvalidRequest represents the according error in RFC 6749
  51. ErrorCodeInvalidRequest AuthorizeErrorCode = "invalid_request"
  52. // ErrorCodeUnauthorizedClient represents the according error in RFC 6749
  53. ErrorCodeUnauthorizedClient AuthorizeErrorCode = "unauthorized_client"
  54. // ErrorCodeAccessDenied represents the according error in RFC 6749
  55. ErrorCodeAccessDenied AuthorizeErrorCode = "access_denied"
  56. // ErrorCodeUnsupportedResponseType represents the according error in RFC 6749
  57. ErrorCodeUnsupportedResponseType AuthorizeErrorCode = "unsupported_response_type"
  58. // ErrorCodeInvalidScope represents the according error in RFC 6749
  59. ErrorCodeInvalidScope AuthorizeErrorCode = "invalid_scope"
  60. // ErrorCodeServerError represents the according error in RFC 6749
  61. ErrorCodeServerError AuthorizeErrorCode = "server_error"
  62. // ErrorCodeTemporaryUnavailable represents the according error in RFC 6749
  63. ErrorCodeTemporaryUnavailable AuthorizeErrorCode = "temporarily_unavailable"
  64. )
  65. // AuthorizeError represents an error type specified in RFC 6749
  66. // https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2.1
  67. type AuthorizeError struct {
  68. ErrorCode AuthorizeErrorCode `json:"error" form:"error"`
  69. ErrorDescription string
  70. State string
  71. }
  72. // Error returns the error message
  73. func (err AuthorizeError) Error() string {
  74. return fmt.Sprintf("%s: %s", err.ErrorCode, err.ErrorDescription)
  75. }
  76. // AccessTokenErrorCode represents an error code specified in RFC 6749
  77. // https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
  78. type AccessTokenErrorCode string
  79. const (
  80. // AccessTokenErrorCodeInvalidRequest represents an error code specified in RFC 6749
  81. AccessTokenErrorCodeInvalidRequest AccessTokenErrorCode = "invalid_request"
  82. // AccessTokenErrorCodeInvalidClient represents an error code specified in RFC 6749
  83. AccessTokenErrorCodeInvalidClient = "invalid_client"
  84. // AccessTokenErrorCodeInvalidGrant represents an error code specified in RFC 6749
  85. AccessTokenErrorCodeInvalidGrant = "invalid_grant"
  86. // AccessTokenErrorCodeUnauthorizedClient represents an error code specified in RFC 6749
  87. AccessTokenErrorCodeUnauthorizedClient = "unauthorized_client"
  88. // AccessTokenErrorCodeUnsupportedGrantType represents an error code specified in RFC 6749
  89. AccessTokenErrorCodeUnsupportedGrantType = "unsupported_grant_type"
  90. // AccessTokenErrorCodeInvalidScope represents an error code specified in RFC 6749
  91. AccessTokenErrorCodeInvalidScope = "invalid_scope"
  92. )
  93. // AccessTokenError represents an error response specified in RFC 6749
  94. // https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
  95. type AccessTokenError struct {
  96. ErrorCode AccessTokenErrorCode `json:"error" form:"error"`
  97. ErrorDescription string `json:"error_description"`
  98. }
  99. // Error returns the error message
  100. func (err AccessTokenError) Error() string {
  101. return fmt.Sprintf("%s: %s", err.ErrorCode, err.ErrorDescription)
  102. }
  103. // errCallback represents a oauth2 callback error
  104. type errCallback struct {
  105. Code string
  106. Description string
  107. }
  108. func (err errCallback) Error() string {
  109. return err.Description
  110. }
  111. // TokenType specifies the kind of token
  112. type TokenType string
  113. const (
  114. // TokenTypeBearer represents a token type specified in RFC 6749
  115. TokenTypeBearer TokenType = "bearer"
  116. // TokenTypeMAC represents a token type specified in RFC 6749
  117. TokenTypeMAC = "mac"
  118. )
  119. // AccessTokenResponse represents a successful access token response
  120. // https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2
  121. type AccessTokenResponse struct {
  122. AccessToken string `json:"access_token"`
  123. TokenType TokenType `json:"token_type"`
  124. ExpiresIn int64 `json:"expires_in"`
  125. RefreshToken string `json:"refresh_token"`
  126. IDToken string `json:"id_token,omitempty"`
  127. }
  128. func newAccessTokenResponse(ctx go_context.Context, grant *auth.OAuth2Grant, serverKey, clientKey oauth2.JWTSigningKey) (*AccessTokenResponse, *AccessTokenError) {
  129. if setting.OAuth2.InvalidateRefreshTokens {
  130. if err := grant.IncreaseCounter(ctx); err != nil {
  131. return nil, &AccessTokenError{
  132. ErrorCode: AccessTokenErrorCodeInvalidGrant,
  133. ErrorDescription: "cannot increase the grant counter",
  134. }
  135. }
  136. }
  137. // generate access token to access the API
  138. expirationDate := timeutil.TimeStampNow().Add(setting.OAuth2.AccessTokenExpirationTime)
  139. accessToken := &oauth2.Token{
  140. GrantID: grant.ID,
  141. Type: oauth2.TypeAccessToken,
  142. RegisteredClaims: jwt.RegisteredClaims{
  143. ExpiresAt: jwt.NewNumericDate(expirationDate.AsTime()),
  144. },
  145. }
  146. signedAccessToken, err := accessToken.SignToken(serverKey)
  147. if err != nil {
  148. return nil, &AccessTokenError{
  149. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  150. ErrorDescription: "cannot sign token",
  151. }
  152. }
  153. // generate refresh token to request an access token after it expired later
  154. refreshExpirationDate := timeutil.TimeStampNow().Add(setting.OAuth2.RefreshTokenExpirationTime * 60 * 60).AsTime()
  155. refreshToken := &oauth2.Token{
  156. GrantID: grant.ID,
  157. Counter: grant.Counter,
  158. Type: oauth2.TypeRefreshToken,
  159. RegisteredClaims: jwt.RegisteredClaims{
  160. ExpiresAt: jwt.NewNumericDate(refreshExpirationDate),
  161. },
  162. }
  163. signedRefreshToken, err := refreshToken.SignToken(serverKey)
  164. if err != nil {
  165. return nil, &AccessTokenError{
  166. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  167. ErrorDescription: "cannot sign token",
  168. }
  169. }
  170. // generate OpenID Connect id_token
  171. signedIDToken := ""
  172. if grant.ScopeContains("openid") {
  173. app, err := auth.GetOAuth2ApplicationByID(ctx, grant.ApplicationID)
  174. if err != nil {
  175. return nil, &AccessTokenError{
  176. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  177. ErrorDescription: "cannot find application",
  178. }
  179. }
  180. user, err := user_model.GetUserByID(ctx, grant.UserID)
  181. if err != nil {
  182. if user_model.IsErrUserNotExist(err) {
  183. return nil, &AccessTokenError{
  184. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  185. ErrorDescription: "cannot find user",
  186. }
  187. }
  188. log.Error("Error loading user: %v", err)
  189. return nil, &AccessTokenError{
  190. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  191. ErrorDescription: "server error",
  192. }
  193. }
  194. idToken := &oauth2.OIDCToken{
  195. RegisteredClaims: jwt.RegisteredClaims{
  196. ExpiresAt: jwt.NewNumericDate(expirationDate.AsTime()),
  197. Issuer: setting.AppURL,
  198. Audience: []string{app.ClientID},
  199. Subject: fmt.Sprint(grant.UserID),
  200. },
  201. Nonce: grant.Nonce,
  202. }
  203. if grant.ScopeContains("profile") {
  204. idToken.Name = user.GetDisplayName()
  205. idToken.PreferredUsername = user.Name
  206. idToken.Profile = user.HTMLURL()
  207. idToken.Picture = user.AvatarLink(ctx)
  208. idToken.Website = user.Website
  209. idToken.Locale = user.Language
  210. idToken.UpdatedAt = user.UpdatedUnix
  211. }
  212. if grant.ScopeContains("email") {
  213. idToken.Email = user.Email
  214. idToken.EmailVerified = user.IsActive
  215. }
  216. if grant.ScopeContains("groups") {
  217. groups, err := getOAuthGroupsForUser(ctx, user)
  218. if err != nil {
  219. log.Error("Error getting groups: %v", err)
  220. return nil, &AccessTokenError{
  221. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  222. ErrorDescription: "server error",
  223. }
  224. }
  225. idToken.Groups = groups
  226. }
  227. signedIDToken, err = idToken.SignToken(clientKey)
  228. if err != nil {
  229. return nil, &AccessTokenError{
  230. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  231. ErrorDescription: "cannot sign token",
  232. }
  233. }
  234. }
  235. return &AccessTokenResponse{
  236. AccessToken: signedAccessToken,
  237. TokenType: TokenTypeBearer,
  238. ExpiresIn: setting.OAuth2.AccessTokenExpirationTime,
  239. RefreshToken: signedRefreshToken,
  240. IDToken: signedIDToken,
  241. }, nil
  242. }
  243. type userInfoResponse struct {
  244. Sub string `json:"sub"`
  245. Name string `json:"name"`
  246. Username string `json:"preferred_username"`
  247. Email string `json:"email"`
  248. Picture string `json:"picture"`
  249. Groups []string `json:"groups"`
  250. }
  251. // InfoOAuth manages request for userinfo endpoint
  252. func InfoOAuth(ctx *context.Context) {
  253. if ctx.Doer == nil || ctx.Data["AuthedMethod"] != (&auth_service.OAuth2{}).Name() {
  254. ctx.Resp.Header().Set("WWW-Authenticate", `Bearer realm=""`)
  255. ctx.PlainText(http.StatusUnauthorized, "no valid authorization")
  256. return
  257. }
  258. response := &userInfoResponse{
  259. Sub: fmt.Sprint(ctx.Doer.ID),
  260. Name: ctx.Doer.FullName,
  261. Username: ctx.Doer.Name,
  262. Email: ctx.Doer.Email,
  263. Picture: ctx.Doer.AvatarLink(ctx),
  264. }
  265. groups, err := getOAuthGroupsForUser(ctx, ctx.Doer)
  266. if err != nil {
  267. ctx.ServerError("Oauth groups for user", err)
  268. return
  269. }
  270. response.Groups = groups
  271. ctx.JSON(http.StatusOK, response)
  272. }
  273. // returns a list of "org" and "org:team" strings,
  274. // that the given user is a part of.
  275. func getOAuthGroupsForUser(ctx go_context.Context, user *user_model.User) ([]string, error) {
  276. orgs, err := org_model.GetUserOrgsList(ctx, user)
  277. if err != nil {
  278. return nil, fmt.Errorf("GetUserOrgList: %w", err)
  279. }
  280. var groups []string
  281. for _, org := range orgs {
  282. groups = append(groups, org.Name)
  283. teams, err := org.LoadTeams()
  284. if err != nil {
  285. return nil, fmt.Errorf("LoadTeams: %w", err)
  286. }
  287. for _, team := range teams {
  288. if team.IsMember(user.ID) {
  289. groups = append(groups, org.Name+":"+team.LowerName)
  290. }
  291. }
  292. }
  293. return groups, nil
  294. }
  295. // IntrospectOAuth introspects an oauth token
  296. func IntrospectOAuth(ctx *context.Context) {
  297. if ctx.Doer == nil {
  298. ctx.Resp.Header().Set("WWW-Authenticate", `Bearer realm=""`)
  299. ctx.PlainText(http.StatusUnauthorized, "no valid authorization")
  300. return
  301. }
  302. var response struct {
  303. Active bool `json:"active"`
  304. Scope string `json:"scope,omitempty"`
  305. jwt.RegisteredClaims
  306. }
  307. form := web.GetForm(ctx).(*forms.IntrospectTokenForm)
  308. token, err := oauth2.ParseToken(form.Token, oauth2.DefaultSigningKey)
  309. if err == nil {
  310. grant, err := auth.GetOAuth2GrantByID(ctx, token.GrantID)
  311. if err == nil && grant != nil {
  312. app, err := auth.GetOAuth2ApplicationByID(ctx, grant.ApplicationID)
  313. if err == nil && app != nil {
  314. response.Active = true
  315. response.Scope = grant.Scope
  316. response.Issuer = setting.AppURL
  317. response.Audience = []string{app.ClientID}
  318. response.Subject = fmt.Sprint(grant.UserID)
  319. }
  320. }
  321. }
  322. ctx.JSON(http.StatusOK, response)
  323. }
  324. // AuthorizeOAuth manages authorize requests
  325. func AuthorizeOAuth(ctx *context.Context) {
  326. form := web.GetForm(ctx).(*forms.AuthorizationForm)
  327. errs := binding.Errors{}
  328. errs = form.Validate(ctx.Req, errs)
  329. if len(errs) > 0 {
  330. errstring := ""
  331. for _, e := range errs {
  332. errstring += e.Error() + "\n"
  333. }
  334. ctx.ServerError("AuthorizeOAuth: Validate: ", fmt.Errorf("errors occurred during validation: %s", errstring))
  335. return
  336. }
  337. app, err := auth.GetOAuth2ApplicationByClientID(ctx, form.ClientID)
  338. if err != nil {
  339. if auth.IsErrOauthClientIDInvalid(err) {
  340. handleAuthorizeError(ctx, AuthorizeError{
  341. ErrorCode: ErrorCodeUnauthorizedClient,
  342. ErrorDescription: "Client ID not registered",
  343. State: form.State,
  344. }, "")
  345. return
  346. }
  347. ctx.ServerError("GetOAuth2ApplicationByClientID", err)
  348. return
  349. }
  350. var user *user_model.User
  351. if app.UID != 0 {
  352. user, err = user_model.GetUserByID(ctx, app.UID)
  353. if err != nil {
  354. ctx.ServerError("GetUserByID", err)
  355. return
  356. }
  357. }
  358. if !app.ContainsRedirectURI(form.RedirectURI) {
  359. handleAuthorizeError(ctx, AuthorizeError{
  360. ErrorCode: ErrorCodeInvalidRequest,
  361. ErrorDescription: "Unregistered Redirect URI",
  362. State: form.State,
  363. }, "")
  364. return
  365. }
  366. if form.ResponseType != "code" {
  367. handleAuthorizeError(ctx, AuthorizeError{
  368. ErrorCode: ErrorCodeUnsupportedResponseType,
  369. ErrorDescription: "Only code response type is supported.",
  370. State: form.State,
  371. }, form.RedirectURI)
  372. return
  373. }
  374. // pkce support
  375. switch form.CodeChallengeMethod {
  376. case "S256":
  377. case "plain":
  378. if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallengeMethod); err != nil {
  379. handleAuthorizeError(ctx, AuthorizeError{
  380. ErrorCode: ErrorCodeServerError,
  381. ErrorDescription: "cannot set code challenge method",
  382. State: form.State,
  383. }, form.RedirectURI)
  384. return
  385. }
  386. if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallenge); err != nil {
  387. handleAuthorizeError(ctx, AuthorizeError{
  388. ErrorCode: ErrorCodeServerError,
  389. ErrorDescription: "cannot set code challenge",
  390. State: form.State,
  391. }, form.RedirectURI)
  392. return
  393. }
  394. // Here we're just going to try to release the session early
  395. if err := ctx.Session.Release(); err != nil {
  396. // we'll tolerate errors here as they *should* get saved elsewhere
  397. log.Error("Unable to save changes to the session: %v", err)
  398. }
  399. case "":
  400. // "Authorization servers SHOULD reject authorization requests from native apps that don't use PKCE by returning an error message"
  401. // https://datatracker.ietf.org/doc/html/rfc8252#section-8.1
  402. if !app.ConfidentialClient {
  403. // "the authorization endpoint MUST return the authorization error response with the "error" value set to "invalid_request""
  404. // https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1
  405. handleAuthorizeError(ctx, AuthorizeError{
  406. ErrorCode: ErrorCodeInvalidRequest,
  407. ErrorDescription: "PKCE is required for public clients",
  408. State: form.State,
  409. }, form.RedirectURI)
  410. return
  411. }
  412. default:
  413. // "If the server supporting PKCE does not support the requested transformation, the authorization endpoint MUST return the authorization error response with "error" value set to "invalid_request"."
  414. // https://www.rfc-editor.org/rfc/rfc7636#section-4.4.1
  415. handleAuthorizeError(ctx, AuthorizeError{
  416. ErrorCode: ErrorCodeInvalidRequest,
  417. ErrorDescription: "unsupported code challenge method",
  418. State: form.State,
  419. }, form.RedirectURI)
  420. return
  421. }
  422. grant, err := app.GetGrantByUserID(ctx, ctx.Doer.ID)
  423. if err != nil {
  424. handleServerError(ctx, form.State, form.RedirectURI)
  425. return
  426. }
  427. // Redirect if user already granted access
  428. if grant != nil {
  429. code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod)
  430. if err != nil {
  431. handleServerError(ctx, form.State, form.RedirectURI)
  432. return
  433. }
  434. redirect, err := code.GenerateRedirectURI(form.State)
  435. if err != nil {
  436. handleServerError(ctx, form.State, form.RedirectURI)
  437. return
  438. }
  439. // Update nonce to reflect the new session
  440. if len(form.Nonce) > 0 {
  441. err := grant.SetNonce(ctx, form.Nonce)
  442. if err != nil {
  443. log.Error("Unable to update nonce: %v", err)
  444. }
  445. }
  446. ctx.Redirect(redirect.String())
  447. return
  448. }
  449. // show authorize page to grant access
  450. ctx.Data["Application"] = app
  451. ctx.Data["RedirectURI"] = form.RedirectURI
  452. ctx.Data["State"] = form.State
  453. ctx.Data["Scope"] = form.Scope
  454. ctx.Data["Nonce"] = form.Nonce
  455. if user != nil {
  456. ctx.Data["ApplicationCreatorLinkHTML"] = fmt.Sprintf(`<a href="%s">@%s</a>`, html.EscapeString(user.HomeLink()), html.EscapeString(user.Name))
  457. } else {
  458. ctx.Data["ApplicationCreatorLinkHTML"] = fmt.Sprintf(`<a href="%s">%s</a>`, html.EscapeString(setting.AppSubURL+"/"), html.EscapeString(setting.AppName))
  459. }
  460. ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + html.EscapeString(form.RedirectURI) + "</strong>"
  461. // TODO document SESSION <=> FORM
  462. err = ctx.Session.Set("client_id", app.ClientID)
  463. if err != nil {
  464. handleServerError(ctx, form.State, form.RedirectURI)
  465. log.Error(err.Error())
  466. return
  467. }
  468. err = ctx.Session.Set("redirect_uri", form.RedirectURI)
  469. if err != nil {
  470. handleServerError(ctx, form.State, form.RedirectURI)
  471. log.Error(err.Error())
  472. return
  473. }
  474. err = ctx.Session.Set("state", form.State)
  475. if err != nil {
  476. handleServerError(ctx, form.State, form.RedirectURI)
  477. log.Error(err.Error())
  478. return
  479. }
  480. // Here we're just going to try to release the session early
  481. if err := ctx.Session.Release(); err != nil {
  482. // we'll tolerate errors here as they *should* get saved elsewhere
  483. log.Error("Unable to save changes to the session: %v", err)
  484. }
  485. ctx.HTML(http.StatusOK, tplGrantAccess)
  486. }
  487. // GrantApplicationOAuth manages the post request submitted when a user grants access to an application
  488. func GrantApplicationOAuth(ctx *context.Context) {
  489. form := web.GetForm(ctx).(*forms.GrantApplicationForm)
  490. if ctx.Session.Get("client_id") != form.ClientID || ctx.Session.Get("state") != form.State ||
  491. ctx.Session.Get("redirect_uri") != form.RedirectURI {
  492. ctx.Error(http.StatusBadRequest)
  493. return
  494. }
  495. app, err := auth.GetOAuth2ApplicationByClientID(ctx, form.ClientID)
  496. if err != nil {
  497. ctx.ServerError("GetOAuth2ApplicationByClientID", err)
  498. return
  499. }
  500. grant, err := app.CreateGrant(ctx, ctx.Doer.ID, form.Scope)
  501. if err != nil {
  502. handleAuthorizeError(ctx, AuthorizeError{
  503. State: form.State,
  504. ErrorDescription: "cannot create grant for user",
  505. ErrorCode: ErrorCodeServerError,
  506. }, form.RedirectURI)
  507. return
  508. }
  509. if len(form.Nonce) > 0 {
  510. err := grant.SetNonce(ctx, form.Nonce)
  511. if err != nil {
  512. log.Error("Unable to update nonce: %v", err)
  513. }
  514. }
  515. var codeChallenge, codeChallengeMethod string
  516. codeChallenge, _ = ctx.Session.Get("CodeChallenge").(string)
  517. codeChallengeMethod, _ = ctx.Session.Get("CodeChallengeMethod").(string)
  518. code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, codeChallenge, codeChallengeMethod)
  519. if err != nil {
  520. handleServerError(ctx, form.State, form.RedirectURI)
  521. return
  522. }
  523. redirect, err := code.GenerateRedirectURI(form.State)
  524. if err != nil {
  525. handleServerError(ctx, form.State, form.RedirectURI)
  526. return
  527. }
  528. ctx.Redirect(redirect.String(), http.StatusSeeOther)
  529. }
  530. // OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities
  531. func OIDCWellKnown(ctx *context.Context) {
  532. t, err := ctx.Render.TemplateLookup("user/auth/oidc_wellknown", nil)
  533. if err != nil {
  534. ctx.ServerError("unable to find template", err)
  535. return
  536. }
  537. ctx.Resp.Header().Set("Content-Type", "application/json")
  538. ctx.Data["SigningKey"] = oauth2.DefaultSigningKey
  539. if err = t.Execute(ctx.Resp, ctx.Data); err != nil {
  540. ctx.ServerError("unable to execute template", err)
  541. }
  542. }
  543. // OIDCKeys generates the JSON Web Key Set
  544. func OIDCKeys(ctx *context.Context) {
  545. jwk, err := oauth2.DefaultSigningKey.ToJWK()
  546. if err != nil {
  547. log.Error("Error converting signing key to JWK: %v", err)
  548. ctx.Error(http.StatusInternalServerError)
  549. return
  550. }
  551. jwk["use"] = "sig"
  552. jwks := map[string][]map[string]string{
  553. "keys": {
  554. jwk,
  555. },
  556. }
  557. ctx.Resp.Header().Set("Content-Type", "application/json")
  558. enc := json.NewEncoder(ctx.Resp)
  559. if err := enc.Encode(jwks); err != nil {
  560. log.Error("Failed to encode representation as json. Error: %v", err)
  561. }
  562. }
  563. // AccessTokenOAuth manages all access token requests by the client
  564. func AccessTokenOAuth(ctx *context.Context) {
  565. form := *web.GetForm(ctx).(*forms.AccessTokenForm)
  566. // if there is no ClientID or ClientSecret in the request body, fill these fields by the Authorization header and ensure the provided field matches the Authorization header
  567. if form.ClientID == "" || form.ClientSecret == "" {
  568. authHeader := ctx.Req.Header.Get("Authorization")
  569. authContent := strings.SplitN(authHeader, " ", 2)
  570. if len(authContent) == 2 && authContent[0] == "Basic" {
  571. payload, err := base64.StdEncoding.DecodeString(authContent[1])
  572. if err != nil {
  573. handleAccessTokenError(ctx, AccessTokenError{
  574. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  575. ErrorDescription: "cannot parse basic auth header",
  576. })
  577. return
  578. }
  579. pair := strings.SplitN(string(payload), ":", 2)
  580. if len(pair) != 2 {
  581. handleAccessTokenError(ctx, AccessTokenError{
  582. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  583. ErrorDescription: "cannot parse basic auth header",
  584. })
  585. return
  586. }
  587. if form.ClientID != "" && form.ClientID != pair[0] {
  588. handleAccessTokenError(ctx, AccessTokenError{
  589. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  590. ErrorDescription: "client_id in request body inconsistent with Authorization header",
  591. })
  592. return
  593. }
  594. form.ClientID = pair[0]
  595. if form.ClientSecret != "" && form.ClientSecret != pair[1] {
  596. handleAccessTokenError(ctx, AccessTokenError{
  597. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  598. ErrorDescription: "client_secret in request body inconsistent with Authorization header",
  599. })
  600. return
  601. }
  602. form.ClientSecret = pair[1]
  603. }
  604. }
  605. serverKey := oauth2.DefaultSigningKey
  606. clientKey := serverKey
  607. if serverKey.IsSymmetric() {
  608. var err error
  609. clientKey, err = oauth2.CreateJWTSigningKey(serverKey.SigningMethod().Alg(), []byte(form.ClientSecret))
  610. if err != nil {
  611. handleAccessTokenError(ctx, AccessTokenError{
  612. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  613. ErrorDescription: "Error creating signing key",
  614. })
  615. return
  616. }
  617. }
  618. switch form.GrantType {
  619. case "refresh_token":
  620. handleRefreshToken(ctx, form, serverKey, clientKey)
  621. case "authorization_code":
  622. handleAuthorizationCode(ctx, form, serverKey, clientKey)
  623. default:
  624. handleAccessTokenError(ctx, AccessTokenError{
  625. ErrorCode: AccessTokenErrorCodeUnsupportedGrantType,
  626. ErrorDescription: "Only refresh_token or authorization_code grant type is supported",
  627. })
  628. }
  629. }
  630. func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm, serverKey, clientKey oauth2.JWTSigningKey) {
  631. app, err := auth.GetOAuth2ApplicationByClientID(ctx, form.ClientID)
  632. if err != nil {
  633. handleAccessTokenError(ctx, AccessTokenError{
  634. ErrorCode: AccessTokenErrorCodeInvalidClient,
  635. ErrorDescription: fmt.Sprintf("cannot load client with client id: %q", form.ClientID),
  636. })
  637. return
  638. }
  639. // "The authorization server MUST ... require client authentication for confidential clients"
  640. // https://datatracker.ietf.org/doc/html/rfc6749#section-6
  641. if app.ConfidentialClient && !app.ValidateClientSecret([]byte(form.ClientSecret)) {
  642. errorDescription := "invalid client secret"
  643. if form.ClientSecret == "" {
  644. errorDescription = "invalid empty client secret"
  645. }
  646. // "invalid_client ... Client authentication failed"
  647. // https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
  648. handleAccessTokenError(ctx, AccessTokenError{
  649. ErrorCode: AccessTokenErrorCodeInvalidClient,
  650. ErrorDescription: errorDescription,
  651. })
  652. return
  653. }
  654. token, err := oauth2.ParseToken(form.RefreshToken, serverKey)
  655. if err != nil {
  656. handleAccessTokenError(ctx, AccessTokenError{
  657. ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
  658. ErrorDescription: "unable to parse refresh token",
  659. })
  660. return
  661. }
  662. // get grant before increasing counter
  663. grant, err := auth.GetOAuth2GrantByID(ctx, token.GrantID)
  664. if err != nil || grant == nil {
  665. handleAccessTokenError(ctx, AccessTokenError{
  666. ErrorCode: AccessTokenErrorCodeInvalidGrant,
  667. ErrorDescription: "grant does not exist",
  668. })
  669. return
  670. }
  671. // check if token got already used
  672. if setting.OAuth2.InvalidateRefreshTokens && (grant.Counter != token.Counter || token.Counter == 0) {
  673. handleAccessTokenError(ctx, AccessTokenError{
  674. ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
  675. ErrorDescription: "token was already used",
  676. })
  677. log.Warn("A client tried to use a refresh token for grant_id = %d was used twice!", grant.ID)
  678. return
  679. }
  680. accessToken, tokenErr := newAccessTokenResponse(ctx, grant, serverKey, clientKey)
  681. if tokenErr != nil {
  682. handleAccessTokenError(ctx, *tokenErr)
  683. return
  684. }
  685. ctx.JSON(http.StatusOK, accessToken)
  686. }
  687. func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, serverKey, clientKey oauth2.JWTSigningKey) {
  688. app, err := auth.GetOAuth2ApplicationByClientID(ctx, form.ClientID)
  689. if err != nil {
  690. handleAccessTokenError(ctx, AccessTokenError{
  691. ErrorCode: AccessTokenErrorCodeInvalidClient,
  692. ErrorDescription: fmt.Sprintf("cannot load client with client id: '%s'", form.ClientID),
  693. })
  694. return
  695. }
  696. if app.ConfidentialClient && !app.ValidateClientSecret([]byte(form.ClientSecret)) {
  697. errorDescription := "invalid client secret"
  698. if form.ClientSecret == "" {
  699. errorDescription = "invalid empty client secret"
  700. }
  701. handleAccessTokenError(ctx, AccessTokenError{
  702. ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
  703. ErrorDescription: errorDescription,
  704. })
  705. return
  706. }
  707. if form.RedirectURI != "" && !app.ContainsRedirectURI(form.RedirectURI) {
  708. handleAccessTokenError(ctx, AccessTokenError{
  709. ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
  710. ErrorDescription: "unexpected redirect URI",
  711. })
  712. return
  713. }
  714. authorizationCode, err := auth.GetOAuth2AuthorizationByCode(ctx, form.Code)
  715. if err != nil || authorizationCode == nil {
  716. handleAccessTokenError(ctx, AccessTokenError{
  717. ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
  718. ErrorDescription: "client is not authorized",
  719. })
  720. return
  721. }
  722. // check if code verifier authorizes the client, PKCE support
  723. if !authorizationCode.ValidateCodeChallenge(form.CodeVerifier) {
  724. handleAccessTokenError(ctx, AccessTokenError{
  725. ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
  726. ErrorDescription: "failed PKCE code challenge",
  727. })
  728. return
  729. }
  730. // check if granted for this application
  731. if authorizationCode.Grant.ApplicationID != app.ID {
  732. handleAccessTokenError(ctx, AccessTokenError{
  733. ErrorCode: AccessTokenErrorCodeInvalidGrant,
  734. ErrorDescription: "invalid grant",
  735. })
  736. return
  737. }
  738. // remove token from database to deny duplicate usage
  739. if err := authorizationCode.Invalidate(ctx); err != nil {
  740. handleAccessTokenError(ctx, AccessTokenError{
  741. ErrorCode: AccessTokenErrorCodeInvalidRequest,
  742. ErrorDescription: "cannot proceed your request",
  743. })
  744. }
  745. resp, tokenErr := newAccessTokenResponse(ctx, authorizationCode.Grant, serverKey, clientKey)
  746. if tokenErr != nil {
  747. handleAccessTokenError(ctx, *tokenErr)
  748. return
  749. }
  750. // send successful response
  751. ctx.JSON(http.StatusOK, resp)
  752. }
  753. func handleAccessTokenError(ctx *context.Context, acErr AccessTokenError) {
  754. ctx.JSON(http.StatusBadRequest, acErr)
  755. }
  756. func handleServerError(ctx *context.Context, state, redirectURI string) {
  757. handleAuthorizeError(ctx, AuthorizeError{
  758. ErrorCode: ErrorCodeServerError,
  759. ErrorDescription: "A server error occurred",
  760. State: state,
  761. }, redirectURI)
  762. }
  763. func handleAuthorizeError(ctx *context.Context, authErr AuthorizeError, redirectURI string) {
  764. if redirectURI == "" {
  765. log.Warn("Authorization failed: %v", authErr.ErrorDescription)
  766. ctx.Data["Error"] = authErr
  767. ctx.HTML(http.StatusBadRequest, tplGrantError)
  768. return
  769. }
  770. redirect, err := url.Parse(redirectURI)
  771. if err != nil {
  772. ctx.ServerError("url.Parse", err)
  773. return
  774. }
  775. q := redirect.Query()
  776. q.Set("error", string(authErr.ErrorCode))
  777. q.Set("error_description", authErr.ErrorDescription)
  778. q.Set("state", authErr.State)
  779. redirect.RawQuery = q.Encode()
  780. ctx.Redirect(redirect.String(), http.StatusSeeOther)
  781. }
  782. // SignInOAuth handles the OAuth2 login buttons
  783. func SignInOAuth(ctx *context.Context) {
  784. provider := ctx.Params(":provider")
  785. authSource, err := auth.GetActiveOAuth2SourceByName(provider)
  786. if err != nil {
  787. ctx.ServerError("SignIn", err)
  788. return
  789. }
  790. redirectTo := ctx.FormString("redirect_to")
  791. if len(redirectTo) > 0 {
  792. middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
  793. }
  794. // try to do a direct callback flow, so we don't authenticate the user again but use the valid accesstoken to get the user
  795. user, gothUser, err := oAuth2UserLoginCallback(ctx, authSource, ctx.Req, ctx.Resp)
  796. if err == nil && user != nil {
  797. // we got the user without going through the whole OAuth2 authentication flow again
  798. handleOAuth2SignIn(ctx, authSource, user, gothUser)
  799. return
  800. }
  801. if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp); err != nil {
  802. if strings.Contains(err.Error(), "no provider for ") {
  803. if err = oauth2.ResetOAuth2(); err != nil {
  804. ctx.ServerError("SignIn", err)
  805. return
  806. }
  807. if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp); err != nil {
  808. ctx.ServerError("SignIn", err)
  809. }
  810. return
  811. }
  812. ctx.ServerError("SignIn", err)
  813. }
  814. // redirect is done in oauth2.Auth
  815. }
  816. // SignInOAuthCallback handles the callback from the given provider
  817. func SignInOAuthCallback(ctx *context.Context) {
  818. provider := ctx.Params(":provider")
  819. if ctx.Req.FormValue("error") != "" {
  820. var errorKeyValues []string
  821. for k, vv := range ctx.Req.Form {
  822. for _, v := range vv {
  823. errorKeyValues = append(errorKeyValues, fmt.Sprintf("%s = %s", html.EscapeString(k), html.EscapeString(v)))
  824. }
  825. }
  826. sort.Strings(errorKeyValues)
  827. ctx.Flash.Error(strings.Join(errorKeyValues, "<br>"), true)
  828. }
  829. // first look if the provider is still active
  830. authSource, err := auth.GetActiveOAuth2SourceByName(provider)
  831. if err != nil {
  832. ctx.ServerError("SignIn", err)
  833. return
  834. }
  835. if authSource == nil {
  836. ctx.ServerError("SignIn", errors.New("no valid provider found, check configured callback url in provider"))
  837. return
  838. }
  839. u, gothUser, err := oAuth2UserLoginCallback(ctx, authSource, ctx.Req, ctx.Resp)
  840. if err != nil {
  841. if user_model.IsErrUserProhibitLogin(err) {
  842. uplerr := err.(user_model.ErrUserProhibitLogin)
  843. log.Info("Failed authentication attempt for %s from %s: %v", uplerr.Name, ctx.RemoteAddr(), err)
  844. ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
  845. ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
  846. return
  847. }
  848. if callbackErr, ok := err.(errCallback); ok {
  849. log.Info("Failed OAuth callback: (%v) %v", callbackErr.Code, callbackErr.Description)
  850. switch callbackErr.Code {
  851. case "access_denied":
  852. ctx.Flash.Error(ctx.Tr("auth.oauth.signin.error.access_denied"))
  853. case "temporarily_unavailable":
  854. ctx.Flash.Error(ctx.Tr("auth.oauth.signin.error.temporarily_unavailable"))
  855. default:
  856. ctx.Flash.Error(ctx.Tr("auth.oauth.signin.error"))
  857. }
  858. ctx.Redirect(setting.AppSubURL + "/user/login")
  859. return
  860. }
  861. if err, ok := err.(*go_oauth2.RetrieveError); ok {
  862. ctx.Flash.Error("OAuth2 RetrieveError: "+err.Error(), true)
  863. }
  864. ctx.ServerError("UserSignIn", err)
  865. return
  866. }
  867. if u == nil {
  868. if ctx.Doer != nil {
  869. // attach user to already logged in user
  870. err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser)
  871. if err != nil {
  872. ctx.ServerError("UserLinkAccount", err)
  873. return
  874. }
  875. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  876. return
  877. } else if !setting.Service.AllowOnlyInternalRegistration && setting.OAuth2Client.EnableAutoRegistration {
  878. // create new user with details from oauth2 provider
  879. var missingFields []string
  880. if gothUser.UserID == "" {
  881. missingFields = append(missingFields, "sub")
  882. }
  883. if gothUser.Email == "" {
  884. missingFields = append(missingFields, "email")
  885. }
  886. if setting.OAuth2Client.Username == setting.OAuth2UsernameNickname && gothUser.NickName == "" {
  887. missingFields = append(missingFields, "nickname")
  888. }
  889. if len(missingFields) > 0 {
  890. log.Error("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields)
  891. if authSource.IsOAuth2() && authSource.Cfg.(*oauth2.Source).Provider == "openidConnect" {
  892. log.Error("You may need to change the 'OPENID_CONNECT_SCOPES' setting to request all required fields")
  893. }
  894. err = fmt.Errorf("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields)
  895. ctx.ServerError("CreateUser", err)
  896. return
  897. }
  898. u = &user_model.User{
  899. Name: getUserName(&gothUser),
  900. FullName: gothUser.Name,
  901. Email: gothUser.Email,
  902. LoginType: auth.OAuth2,
  903. LoginSource: authSource.ID,
  904. LoginName: gothUser.UserID,
  905. }
  906. overwriteDefault := &user_model.CreateUserOverwriteOptions{
  907. IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm),
  908. }
  909. source := authSource.Cfg.(*oauth2.Source)
  910. setUserAdminAndRestrictedFromGroupClaims(source, u, &gothUser)
  911. if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) {
  912. // error already handled
  913. return
  914. }
  915. if err := syncGroupsToTeams(ctx, source, &gothUser, u); err != nil {
  916. ctx.ServerError("SyncGroupsToTeams", err)
  917. return
  918. }
  919. } else {
  920. // no existing user is found, request attach or new account
  921. showLinkingLogin(ctx, gothUser)
  922. return
  923. }
  924. }
  925. handleOAuth2SignIn(ctx, authSource, u, gothUser)
  926. }
  927. func claimValueToStringSet(claimValue any) container.Set[string] {
  928. var groups []string
  929. switch rawGroup := claimValue.(type) {
  930. case []string:
  931. groups = rawGroup
  932. case []any:
  933. for _, group := range rawGroup {
  934. groups = append(groups, fmt.Sprintf("%s", group))
  935. }
  936. default:
  937. str := fmt.Sprintf("%s", rawGroup)
  938. groups = strings.Split(str, ",")
  939. }
  940. return container.SetOf(groups...)
  941. }
  942. func syncGroupsToTeams(ctx *context.Context, source *oauth2.Source, gothUser *goth.User, u *user_model.User) error {
  943. if source.GroupTeamMap != "" || source.GroupTeamMapRemoval {
  944. groupTeamMapping, err := auth_module.UnmarshalGroupTeamMapping(source.GroupTeamMap)
  945. if err != nil {
  946. return err
  947. }
  948. groups := getClaimedGroups(source, gothUser)
  949. if err := source_service.SyncGroupsToTeams(ctx, u, groups, groupTeamMapping, source.GroupTeamMapRemoval); err != nil {
  950. return err
  951. }
  952. }
  953. return nil
  954. }
  955. func getClaimedGroups(source *oauth2.Source, gothUser *goth.User) container.Set[string] {
  956. groupClaims, has := gothUser.RawData[source.GroupClaimName]
  957. if !has {
  958. return nil
  959. }
  960. return claimValueToStringSet(groupClaims)
  961. }
  962. func setUserAdminAndRestrictedFromGroupClaims(source *oauth2.Source, u *user_model.User, gothUser *goth.User) bool {
  963. groups := getClaimedGroups(source, gothUser)
  964. wasAdmin, wasRestricted := u.IsAdmin, u.IsRestricted
  965. if source.AdminGroup != "" {
  966. u.IsAdmin = groups.Contains(source.AdminGroup)
  967. }
  968. if source.RestrictedGroup != "" {
  969. u.IsRestricted = groups.Contains(source.RestrictedGroup)
  970. }
  971. return wasAdmin != u.IsAdmin || wasRestricted != u.IsRestricted
  972. }
  973. func showLinkingLogin(ctx *context.Context, gothUser goth.User) {
  974. if err := updateSession(ctx, nil, map[string]any{
  975. "linkAccountGothUser": gothUser,
  976. }); err != nil {
  977. ctx.ServerError("updateSession", err)
  978. return
  979. }
  980. ctx.Redirect(setting.AppSubURL + "/user/link_account")
  981. }
  982. func updateAvatarIfNeed(url string, u *user_model.User) {
  983. if setting.OAuth2Client.UpdateAvatar && len(url) > 0 {
  984. resp, err := http.Get(url)
  985. if err == nil {
  986. defer func() {
  987. _ = resp.Body.Close()
  988. }()
  989. }
  990. // ignore any error
  991. if err == nil && resp.StatusCode == http.StatusOK {
  992. data, err := io.ReadAll(io.LimitReader(resp.Body, setting.Avatar.MaxFileSize+1))
  993. if err == nil && int64(len(data)) <= setting.Avatar.MaxFileSize {
  994. _ = user_service.UploadAvatar(u, data)
  995. }
  996. }
  997. }
  998. }
  999. func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model.User, gothUser goth.User) {
  1000. updateAvatarIfNeed(gothUser.AvatarURL, u)
  1001. needs2FA := false
  1002. if !source.Cfg.(*oauth2.Source).SkipLocalTwoFA {
  1003. _, err := auth.GetTwoFactorByUID(ctx, u.ID)
  1004. if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
  1005. ctx.ServerError("UserSignIn", err)
  1006. return
  1007. }
  1008. needs2FA = err == nil
  1009. }
  1010. oauth2Source := source.Cfg.(*oauth2.Source)
  1011. groupTeamMapping, err := auth_module.UnmarshalGroupTeamMapping(oauth2Source.GroupTeamMap)
  1012. if err != nil {
  1013. ctx.ServerError("UnmarshalGroupTeamMapping", err)
  1014. return
  1015. }
  1016. groups := getClaimedGroups(oauth2Source, &gothUser)
  1017. // If this user is enrolled in 2FA and this source doesn't override it,
  1018. // we can't sign the user in just yet. Instead, redirect them to the 2FA authentication page.
  1019. if !needs2FA {
  1020. if err := updateSession(ctx, nil, map[string]any{
  1021. "uid": u.ID,
  1022. "uname": u.Name,
  1023. }); err != nil {
  1024. ctx.ServerError("updateSession", err)
  1025. return
  1026. }
  1027. // Clear whatever CSRF cookie has right now, force to generate a new one
  1028. ctx.Csrf.DeleteCookie(ctx)
  1029. // Register last login
  1030. u.SetLastLogin()
  1031. // Update GroupClaims
  1032. changed := setUserAdminAndRestrictedFromGroupClaims(oauth2Source, u, &gothUser)
  1033. cols := []string{"last_login_unix"}
  1034. if changed {
  1035. cols = append(cols, "is_admin", "is_restricted")
  1036. }
  1037. if err := user_model.UpdateUserCols(ctx, u, cols...); err != nil {
  1038. ctx.ServerError("UpdateUserCols", err)
  1039. return
  1040. }
  1041. if oauth2Source.GroupTeamMap != "" || oauth2Source.GroupTeamMapRemoval {
  1042. if err := source_service.SyncGroupsToTeams(ctx, u, groups, groupTeamMapping, oauth2Source.GroupTeamMapRemoval); err != nil {
  1043. ctx.ServerError("SyncGroupsToTeams", err)
  1044. return
  1045. }
  1046. }
  1047. // update external user information
  1048. if err := externalaccount.UpdateExternalUser(u, gothUser); err != nil {
  1049. if !errors.Is(err, util.ErrNotExist) {
  1050. log.Error("UpdateExternalUser failed: %v", err)
  1051. }
  1052. }
  1053. if err := resetLocale(ctx, u); err != nil {
  1054. ctx.ServerError("resetLocale", err)
  1055. return
  1056. }
  1057. if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 {
  1058. middleware.DeleteRedirectToCookie(ctx.Resp)
  1059. ctx.RedirectToFirst(redirectTo)
  1060. return
  1061. }
  1062. ctx.Redirect(setting.AppSubURL + "/")
  1063. return
  1064. }
  1065. changed := setUserAdminAndRestrictedFromGroupClaims(oauth2Source, u, &gothUser)
  1066. if changed {
  1067. if err := user_model.UpdateUserCols(ctx, u, "is_admin", "is_restricted"); err != nil {
  1068. ctx.ServerError("UpdateUserCols", err)
  1069. return
  1070. }
  1071. }
  1072. if oauth2Source.GroupTeamMap != "" || oauth2Source.GroupTeamMapRemoval {
  1073. if err := source_service.SyncGroupsToTeams(ctx, u, groups, groupTeamMapping, oauth2Source.GroupTeamMapRemoval); err != nil {
  1074. ctx.ServerError("SyncGroupsToTeams", err)
  1075. return
  1076. }
  1077. }
  1078. if err := updateSession(ctx, nil, map[string]any{
  1079. // User needs to use 2FA, save data and redirect to 2FA page.
  1080. "twofaUid": u.ID,
  1081. "twofaRemember": false,
  1082. }); err != nil {
  1083. ctx.ServerError("updateSession", err)
  1084. return
  1085. }
  1086. // If WebAuthn is enrolled -> Redirect to WebAuthn instead
  1087. regs, err := auth.GetWebAuthnCredentialsByUID(ctx, u.ID)
  1088. if err == nil && len(regs) > 0 {
  1089. ctx.Redirect(setting.AppSubURL + "/user/webauthn")
  1090. return
  1091. }
  1092. ctx.Redirect(setting.AppSubURL + "/user/two_factor")
  1093. }
  1094. // OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful
  1095. // login the user
  1096. func oAuth2UserLoginCallback(ctx *context.Context, authSource *auth.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) {
  1097. oauth2Source := authSource.Cfg.(*oauth2.Source)
  1098. // Make sure that the response is not an error response.
  1099. errorName := request.FormValue("error")
  1100. if len(errorName) > 0 {
  1101. errorDescription := request.FormValue("error_description")
  1102. // Delete the goth session
  1103. err := gothic.Logout(response, request)
  1104. if err != nil {
  1105. return nil, goth.User{}, err
  1106. }
  1107. return nil, goth.User{}, errCallback{
  1108. Code: errorName,
  1109. Description: errorDescription,
  1110. }
  1111. }
  1112. // Proceed to authenticate through goth.
  1113. gothUser, err := oauth2Source.Callback(request, response)
  1114. if err != nil {
  1115. if err.Error() == "securecookie: the value is too long" || strings.Contains(err.Error(), "Data too long") {
  1116. log.Error("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength)
  1117. err = fmt.Errorf("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength)
  1118. }
  1119. return nil, goth.User{}, err
  1120. }
  1121. if oauth2Source.RequiredClaimName != "" {
  1122. claimInterface, has := gothUser.RawData[oauth2Source.RequiredClaimName]
  1123. if !has {
  1124. return nil, goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID}
  1125. }
  1126. if oauth2Source.RequiredClaimValue != "" {
  1127. groups := claimValueToStringSet(claimInterface)
  1128. if !groups.Contains(oauth2Source.RequiredClaimValue) {
  1129. return nil, goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID}
  1130. }
  1131. }
  1132. }
  1133. user := &user_model.User{
  1134. LoginName: gothUser.UserID,
  1135. LoginType: auth.OAuth2,
  1136. LoginSource: authSource.ID,
  1137. }
  1138. hasUser, err := user_model.GetUser(ctx, user)
  1139. if err != nil {
  1140. return nil, goth.User{}, err
  1141. }
  1142. if hasUser {
  1143. return user, gothUser, nil
  1144. }
  1145. // search in external linked users
  1146. externalLoginUser := &user_model.ExternalLoginUser{
  1147. ExternalID: gothUser.UserID,
  1148. LoginSourceID: authSource.ID,
  1149. }
  1150. hasUser, err = user_model.GetExternalLogin(externalLoginUser)
  1151. if err != nil {
  1152. return nil, goth.User{}, err
  1153. }
  1154. if hasUser {
  1155. user, err = user_model.GetUserByID(request.Context(), externalLoginUser.UserID)
  1156. return user, gothUser, err
  1157. }
  1158. // no user found to login
  1159. return nil, gothUser, nil
  1160. }