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.

oauth.go 39KB

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