builder.go 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390
  1. package login
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "io/fs"
  7. "log"
  8. "net/http"
  9. "reflect"
  10. "strings"
  11. "time"
  12. "github.com/golang-jwt/jwt/v4"
  13. "github.com/markbates/goth"
  14. "github.com/markbates/goth/gothic"
  15. "github.com/pquerna/otp"
  16. "github.com/pquerna/otp/totp"
  17. "github.com/qor5/web"
  18. "github.com/qor5/x/i18n"
  19. h "github.com/theplant/htmlgo"
  20. "golang.org/x/text/language"
  21. "gorm.io/gorm"
  22. )
  23. var (
  24. ErrUserNotFound = errors.New("user not found")
  25. ErrUserPassChanged = errors.New("password changed")
  26. ErrWrongPassword = errors.New("wrong password")
  27. ErrUserLocked = errors.New("user locked")
  28. ErrUserGetLocked = errors.New("user get locked")
  29. ErrWrongTOTPCode = errors.New("wrong totp code")
  30. ErrTOTPCodeHasBeenUsed = errors.New("totp code has been used")
  31. ErrEmptyPassword = errors.New("empty password")
  32. ErrPasswordNotMatch = errors.New("password not match")
  33. )
  34. type HomeURLFunc func(r *http.Request, user interface{}) string
  35. type NotifyUserOfResetPasswordLinkFunc func(user interface{}, resetLink string) error
  36. type PasswordValidationFunc func(password string) (message string, ok bool)
  37. type HookFunc func(r *http.Request, user interface{}, vals ...interface{}) error
  38. type void struct{}
  39. type Provider struct {
  40. Goth goth.Provider
  41. Key string
  42. Text string
  43. Logo h.HTMLComponent
  44. }
  45. type CookieConfig struct {
  46. Path string
  47. Domain string
  48. SameSite http.SameSite
  49. }
  50. type RecaptchaConfig struct {
  51. SiteKey string
  52. SecretKey string
  53. }
  54. type Builder struct {
  55. secret string
  56. providers []*Provider
  57. authCookieName string
  58. authSecureCookieName string
  59. continueUrlCookieName string
  60. // seconds
  61. sessionMaxAge int
  62. cookieConfig CookieConfig
  63. recaptchaEnabled bool
  64. recaptchaConfig RecaptchaConfig
  65. autoExtendSession bool
  66. maxRetryCount int
  67. noForgetPasswordLink bool
  68. // Common URLs
  69. homePageURLFunc HomeURLFunc
  70. loginPageURL string
  71. LogoutURL string
  72. allowURLs map[string]void
  73. // TOTP URLs
  74. validateTOTPURL string
  75. totpSetupPageURL string
  76. totpValidatePageURL string
  77. // OAuth URLs
  78. oauthBeginURL string
  79. oauthCallbackURL string
  80. oauthCallbackCompleteURL string
  81. // UserPass URLs
  82. passwordLoginURL string
  83. resetPasswordURL string
  84. resetPasswordPageURL string
  85. changePasswordURL string
  86. changePasswordPageURL string
  87. forgetPasswordPageURL string
  88. sendResetPasswordLinkURL string
  89. resetPasswordLinkSentPageURL string
  90. loginPageFunc web.PageFunc
  91. forgetPasswordPageFunc web.PageFunc
  92. resetPasswordLinkSentPageFunc web.PageFunc
  93. resetPasswordPageFunc web.PageFunc
  94. changePasswordPageFunc web.PageFunc
  95. totpSetupPageFunc web.PageFunc
  96. totpValidatePageFunc web.PageFunc
  97. notifyUserOfResetPasswordLinkFunc NotifyUserOfResetPasswordLinkFunc
  98. passwordValidationFunc PasswordValidationFunc
  99. afterLoginHook HookFunc
  100. afterFailedToLoginHook HookFunc
  101. afterUserLockedHook HookFunc
  102. afterLogoutHook HookFunc
  103. afterSendResetPasswordLinkHook HookFunc
  104. afterResetPasswordHook HookFunc
  105. afterChangePasswordHook HookFunc
  106. afterExtendSessionHook HookFunc
  107. afterTOTPCodeReusedHook HookFunc
  108. afterOAuthCompleteHook HookFunc
  109. db *gorm.DB
  110. userModel interface{}
  111. snakePrimaryField string
  112. tUser reflect.Type
  113. userPassEnabled bool
  114. oauthEnabled bool
  115. sessionSecureEnabled bool
  116. totpEnabled bool
  117. totpIssuer string
  118. i18nBuilder *i18n.Builder
  119. }
  120. func New() *Builder {
  121. r := &Builder{
  122. authCookieName: "auth",
  123. authSecureCookieName: "qor5_auth_secure",
  124. continueUrlCookieName: "qor5_continue_url",
  125. homePageURLFunc: func(r *http.Request, user interface{}) string {
  126. return "/"
  127. },
  128. loginPageURL: "/auth/login",
  129. LogoutURL: "/auth/logout",
  130. validateTOTPURL: "/auth/2fa/totp/do",
  131. totpSetupPageURL: "/auth/2fa/totp/setup",
  132. totpValidatePageURL: "/auth/2fa/totp/validate",
  133. oauthBeginURL: "/auth/begin",
  134. oauthCallbackURL: "/auth/callback",
  135. oauthCallbackCompleteURL: "/auth/callback-complete",
  136. passwordLoginURL: "/auth/userpass/login",
  137. resetPasswordURL: "/auth/do-reset-password",
  138. resetPasswordPageURL: "/auth/reset-password",
  139. changePasswordURL: "/auth/do-change-password",
  140. changePasswordPageURL: "/auth/change-password",
  141. forgetPasswordPageURL: "/auth/forget-password",
  142. sendResetPasswordLinkURL: "/auth/send-reset-password-link",
  143. resetPasswordLinkSentPageURL: "/auth/reset-password-link-sent",
  144. sessionMaxAge: 60 * 60,
  145. cookieConfig: CookieConfig{
  146. Path: "/",
  147. Domain: "",
  148. SameSite: http.SameSiteStrictMode,
  149. },
  150. autoExtendSession: true,
  151. maxRetryCount: 5,
  152. totpEnabled: true,
  153. totpIssuer: "qor5",
  154. i18nBuilder: i18n.New(),
  155. }
  156. r.registerI18n()
  157. r.initAllowURLs()
  158. vh := r.ViewHelper()
  159. r.loginPageFunc = defaultLoginPage(vh)
  160. r.forgetPasswordPageFunc = defaultForgetPasswordPage(vh)
  161. r.resetPasswordLinkSentPageFunc = defaultResetPasswordLinkSentPage(vh)
  162. r.resetPasswordPageFunc = defaultResetPasswordPage(vh)
  163. r.changePasswordPageFunc = defaultChangePasswordPage(vh)
  164. r.totpSetupPageFunc = defaultTOTPSetupPage(vh)
  165. r.totpValidatePageFunc = defaultTOTPValidatePage(vh)
  166. return r
  167. }
  168. func (b *Builder) initAllowURLs() {
  169. b.allowURLs = map[string]void{
  170. b.oauthBeginURL: {},
  171. b.oauthCallbackURL: {},
  172. b.oauthCallbackCompleteURL: {},
  173. b.passwordLoginURL: {},
  174. b.forgetPasswordPageURL: {},
  175. b.sendResetPasswordLinkURL: {},
  176. b.resetPasswordLinkSentPageURL: {},
  177. b.resetPasswordURL: {},
  178. b.resetPasswordPageURL: {},
  179. b.validateTOTPURL: {},
  180. }
  181. }
  182. func (b *Builder) AllowURL(v string) {
  183. b.allowURLs[v] = void{}
  184. }
  185. func (b *Builder) Secret(v string) (r *Builder) {
  186. b.secret = v
  187. return b
  188. }
  189. func (b *Builder) CookieConfig(v CookieConfig) (r *Builder) {
  190. b.cookieConfig = v
  191. return b
  192. }
  193. // RecaptchaConfig should be set if you want to enable Google reCAPTCHA.
  194. func (b *Builder) RecaptchaConfig(v RecaptchaConfig) (r *Builder) {
  195. b.recaptchaConfig = v
  196. b.recaptchaEnabled = b.recaptchaConfig.SiteKey != "" && b.recaptchaConfig.SecretKey != ""
  197. return b
  198. }
  199. func (b *Builder) OAuthProviders(vs ...*Provider) (r *Builder) {
  200. if len(vs) == 0 {
  201. return b
  202. }
  203. b.oauthEnabled = true
  204. b.providers = vs
  205. var gothProviders []goth.Provider
  206. for _, v := range vs {
  207. gothProviders = append(gothProviders, v.Goth)
  208. }
  209. goth.UseProviders(gothProviders...)
  210. return b
  211. }
  212. func (b *Builder) AuthCookieName(v string) (r *Builder) {
  213. b.authCookieName = v
  214. return b
  215. }
  216. func (b *Builder) LoginURL(v string) (r *Builder) {
  217. b.loginPageURL = v
  218. return b
  219. }
  220. func (b *Builder) HomeURLFunc(v HomeURLFunc) (r *Builder) {
  221. b.homePageURLFunc = v
  222. return b
  223. }
  224. func (b *Builder) LoginPageFunc(v web.PageFunc) (r *Builder) {
  225. b.loginPageFunc = v
  226. return b
  227. }
  228. func (b *Builder) ForgetPasswordPageFunc(v web.PageFunc) (r *Builder) {
  229. b.forgetPasswordPageFunc = v
  230. return b
  231. }
  232. func (b *Builder) ResetPasswordLinkSentPageFunc(v web.PageFunc) (r *Builder) {
  233. b.resetPasswordLinkSentPageFunc = v
  234. return b
  235. }
  236. func (b *Builder) ResetPasswordPageFunc(v web.PageFunc) (r *Builder) {
  237. b.resetPasswordPageFunc = v
  238. return b
  239. }
  240. func (b *Builder) ChangePasswordPageFunc(v web.PageFunc) (r *Builder) {
  241. b.changePasswordPageFunc = v
  242. return b
  243. }
  244. func (b *Builder) TOTPSetupPageFunc(v web.PageFunc) (r *Builder) {
  245. b.totpSetupPageFunc = v
  246. return b
  247. }
  248. func (b *Builder) TOTPValidatePageFunc(v web.PageFunc) (r *Builder) {
  249. b.totpValidatePageFunc = v
  250. return b
  251. }
  252. func (b *Builder) NotifyUserOfResetPasswordLinkFunc(v NotifyUserOfResetPasswordLinkFunc) (r *Builder) {
  253. b.notifyUserOfResetPasswordLinkFunc = v
  254. return b
  255. }
  256. func (b *Builder) PasswordValidationFunc(v PasswordValidationFunc) (r *Builder) {
  257. b.passwordValidationFunc = v
  258. return b
  259. }
  260. func (b *Builder) wrapHook(v HookFunc) HookFunc {
  261. if v == nil {
  262. return nil
  263. }
  264. return func(r *http.Request, user interface{}, vals ...interface{}) error {
  265. if GetCurrentUser(r) == nil {
  266. r = r.WithContext(context.WithValue(r.Context(), UserKey, user))
  267. }
  268. return v(r, user, vals...)
  269. }
  270. }
  271. func (b *Builder) AfterLogin(v HookFunc) (r *Builder) {
  272. b.afterLoginHook = b.wrapHook(v)
  273. return b
  274. }
  275. func (b *Builder) AfterFailedToLogin(v HookFunc) (r *Builder) {
  276. b.afterFailedToLoginHook = b.wrapHook(v)
  277. return b
  278. }
  279. func (b *Builder) AfterUserLocked(v HookFunc) (r *Builder) {
  280. b.afterUserLockedHook = b.wrapHook(v)
  281. return b
  282. }
  283. func (b *Builder) AfterLogout(v HookFunc) (r *Builder) {
  284. b.afterLogoutHook = b.wrapHook(v)
  285. return b
  286. }
  287. func (b *Builder) AfterSendResetPasswordLink(v HookFunc) (r *Builder) {
  288. b.afterSendResetPasswordLinkHook = b.wrapHook(v)
  289. return b
  290. }
  291. func (b *Builder) AfterResetPassword(v HookFunc) (r *Builder) {
  292. b.afterResetPasswordHook = b.wrapHook(v)
  293. return b
  294. }
  295. func (b *Builder) AfterChangePassword(v HookFunc) (r *Builder) {
  296. b.afterChangePasswordHook = b.wrapHook(v)
  297. return b
  298. }
  299. // vals:
  300. // - old session token
  301. func (b *Builder) AfterExtendSession(v HookFunc) (r *Builder) {
  302. b.afterExtendSessionHook = b.wrapHook(v)
  303. return b
  304. }
  305. func (b *Builder) AfterTOTPCodeReused(v HookFunc) (r *Builder) {
  306. b.afterTOTPCodeReusedHook = b.wrapHook(v)
  307. return b
  308. }
  309. // user is goth.User
  310. func (b *Builder) AfterOAuthComplete(v HookFunc) (r *Builder) {
  311. b.afterOAuthCompleteHook = b.wrapHook(v)
  312. return b
  313. }
  314. // seconds
  315. // default 1h
  316. func (b *Builder) SessionMaxAge(v int) (r *Builder) {
  317. b.sessionMaxAge = v
  318. return b
  319. }
  320. // extend the session if successfully authenticated
  321. // default true
  322. func (b *Builder) AutoExtendSession(v bool) (r *Builder) {
  323. b.autoExtendSession = v
  324. return b
  325. }
  326. // default 5
  327. // MaxRetryCount <= 0 means no max retry count limit
  328. func (b *Builder) MaxRetryCount(v int) (r *Builder) {
  329. b.maxRetryCount = v
  330. return b
  331. }
  332. func (b *Builder) TOTPEnabled(v bool) (r *Builder) {
  333. b.totpEnabled = v
  334. return b
  335. }
  336. func (b *Builder) TOTPIssuer(v string) (r *Builder) {
  337. b.totpIssuer = v
  338. return b
  339. }
  340. func (b *Builder) NoForgetPasswordLink(v bool) (r *Builder) {
  341. b.noForgetPasswordLink = v
  342. return b
  343. }
  344. func (b *Builder) DB(v *gorm.DB) (r *Builder) {
  345. b.db = v
  346. return b
  347. }
  348. func (b *Builder) I18n(v *i18n.Builder) (r *Builder) {
  349. b.i18nBuilder = v
  350. b.registerI18n()
  351. return b
  352. }
  353. func (b *Builder) GetSessionMaxAge() int {
  354. return b.sessionMaxAge
  355. }
  356. func (b *Builder) ViewHelper() *ViewHelper {
  357. return &ViewHelper{
  358. b: b,
  359. }
  360. }
  361. func (b *Builder) registerI18n() {
  362. b.i18nBuilder.RegisterForModule(language.English, I18nLoginKey, Messages_en_US).
  363. RegisterForModule(language.SimplifiedChinese, I18nLoginKey, Messages_zh_CN).
  364. RegisterForModule(language.Japanese, I18nLoginKey, Messages_ja_JP)
  365. }
  366. func (b *Builder) UserModel(m interface{}) (r *Builder) {
  367. b.userModel = m
  368. b.tUser = underlyingReflectType(reflect.TypeOf(m))
  369. b.snakePrimaryField = snakePrimaryField(m)
  370. if _, ok := m.(UserPasser); ok {
  371. b.userPassEnabled = true
  372. }
  373. if _, ok := m.(OAuthUser); ok {
  374. b.oauthEnabled = true
  375. }
  376. if _, ok := m.(SessionSecurer); ok {
  377. b.sessionSecureEnabled = true
  378. }
  379. return b
  380. }
  381. func (b *Builder) newUserObject() interface{} {
  382. return reflect.New(b.tUser).Interface()
  383. }
  384. func (b *Builder) findUserByID(id string) (user interface{}, err error) {
  385. m := b.newUserObject()
  386. err = b.db.Where(fmt.Sprintf("%s = ?", b.snakePrimaryField), id).
  387. First(m).
  388. Error
  389. if err != nil {
  390. if err == gorm.ErrRecordNotFound {
  391. return nil, ErrUserNotFound
  392. }
  393. return nil, err
  394. }
  395. return m, nil
  396. }
  397. // completeUserAuthCallback is for url "/auth/{provider}/callback"
  398. func (b *Builder) completeUserAuthCallback(w http.ResponseWriter, r *http.Request) {
  399. if b.cookieConfig.SameSite != http.SameSiteStrictMode {
  400. b.completeUserAuthCallbackComplete(w, r)
  401. return
  402. }
  403. completeURL := fmt.Sprintf("%s?%s", b.oauthCallbackCompleteURL, r.URL.Query().Encode())
  404. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  405. w.Write([]byte(fmt.Sprintf(`
  406. <script>
  407. window.location.href="%s";
  408. </script>
  409. <a href="%s">complete</a>
  410. `, completeURL, completeURL)))
  411. return
  412. }
  413. func (b *Builder) completeUserAuthCallbackComplete(w http.ResponseWriter, r *http.Request) {
  414. var err error
  415. var user interface{}
  416. defer func() {
  417. if b.afterFailedToLoginHook != nil && err != nil && user != nil {
  418. b.afterFailedToLoginHook(r, user)
  419. }
  420. }()
  421. var ouser goth.User
  422. ouser, err = gothic.CompleteUserAuth(w, r)
  423. if err != nil {
  424. log.Println("completeUserAuthWithSetCookie", err)
  425. setFailCodeFlash(w, FailCodeCompleteUserAuthFailed)
  426. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  427. return
  428. }
  429. if b.afterOAuthCompleteHook != nil {
  430. if herr := b.afterOAuthCompleteHook(r, ouser); herr != nil {
  431. setFailCodeFlash(w, FailCodeSystemError)
  432. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  433. return
  434. }
  435. }
  436. userID := ouser.UserID
  437. if b.userModel != nil {
  438. user, err = b.userModel.(OAuthUser).FindUserByOAuthUserID(b.db, b.newUserObject(), ouser.Provider, ouser.UserID)
  439. if err != nil {
  440. if err != gorm.ErrRecordNotFound {
  441. setFailCodeFlash(w, FailCodeSystemError)
  442. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  443. return
  444. }
  445. // TODO: maybe the indentifier of some providers is not email
  446. indentifier := ouser.Email
  447. user, err = b.userModel.(OAuthUser).FindUserByOAuthIndentifier(b.db, b.newUserObject(), ouser.Provider, indentifier)
  448. if err != nil {
  449. if err == gorm.ErrRecordNotFound {
  450. setFailCodeFlash(w, FailCodeUserNotFound)
  451. } else {
  452. setFailCodeFlash(w, FailCodeSystemError)
  453. }
  454. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  455. return
  456. }
  457. err = user.(OAuthUser).InitOAuthUserID(b.db, b.newUserObject(), ouser.Provider, indentifier, ouser.UserID)
  458. if err != nil {
  459. setFailCodeFlash(w, FailCodeSystemError)
  460. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  461. return
  462. }
  463. }
  464. userID = objectID(user)
  465. }
  466. claims := UserClaims{
  467. Provider: ouser.Provider,
  468. Email: ouser.Email,
  469. Name: ouser.Name,
  470. UserID: userID,
  471. AvatarURL: ouser.AvatarURL,
  472. RegisteredClaims: b.genBaseSessionClaim(userID),
  473. }
  474. if user == nil {
  475. user = &claims
  476. }
  477. if b.afterLoginHook != nil {
  478. setCookieForRequest(r, &http.Cookie{Name: b.authCookieName, Value: b.mustGetSessionToken(claims)})
  479. if herr := b.afterLoginHook(r, user); herr != nil {
  480. setFailCodeFlash(w, FailCodeSystemError)
  481. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  482. return
  483. }
  484. }
  485. if err := b.setSecureCookiesByClaims(w, user, claims); err != nil {
  486. setFailCodeFlash(w, FailCodeSystemError)
  487. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  488. return
  489. }
  490. redirectURL := b.homePageURLFunc(r, user)
  491. if v := b.getContinueURL(w, r); v != "" {
  492. redirectURL = v
  493. }
  494. http.Redirect(w, r, redirectURL, http.StatusFound)
  495. return
  496. }
  497. // return user if account exists even if there is an error returned
  498. func (b *Builder) authUserPass(account string, password string) (user interface{}, err error) {
  499. user, err = b.userModel.(UserPasser).FindUser(b.db, b.newUserObject(), account)
  500. if err != nil {
  501. if err == gorm.ErrRecordNotFound {
  502. return nil, ErrUserNotFound
  503. }
  504. return nil, err
  505. }
  506. u := user.(UserPasser)
  507. if u.GetLocked() {
  508. return user, ErrUserLocked
  509. }
  510. if !u.IsPasswordCorrect(password) {
  511. if b.maxRetryCount > 0 {
  512. if err = u.IncreaseRetryCount(b.db, b.newUserObject()); err != nil {
  513. return user, err
  514. }
  515. if u.GetLoginRetryCount() >= b.maxRetryCount {
  516. if err = u.LockUser(b.db, b.newUserObject()); err != nil {
  517. return user, err
  518. }
  519. return user, ErrUserGetLocked
  520. }
  521. }
  522. return user, ErrWrongPassword
  523. }
  524. if u.GetLoginRetryCount() != 0 {
  525. if err = u.UnlockUser(b.db, b.newUserObject()); err != nil {
  526. return user, err
  527. }
  528. }
  529. return user, nil
  530. }
  531. func (b *Builder) userpassLogin(w http.ResponseWriter, r *http.Request) {
  532. if r.Method != http.MethodPost {
  533. w.WriteHeader(http.StatusNotFound)
  534. return
  535. }
  536. // check reCAPTCHA token
  537. if b.recaptchaEnabled {
  538. token := r.FormValue("token")
  539. if !recaptchaTokenCheck(b, token) {
  540. setFailCodeFlash(w, FailCodeIncorrectRecaptchaToken)
  541. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  542. return
  543. }
  544. }
  545. var err error
  546. var user interface{}
  547. defer func() {
  548. if b.afterFailedToLoginHook != nil && err != nil && user != nil {
  549. b.afterFailedToLoginHook(r, user)
  550. }
  551. }()
  552. account := r.FormValue("account")
  553. password := r.FormValue("password")
  554. user, err = b.authUserPass(account, password)
  555. if err != nil {
  556. if err == ErrUserGetLocked && b.afterUserLockedHook != nil {
  557. if herr := b.afterUserLockedHook(r, user); herr != nil {
  558. setFailCodeFlash(w, FailCodeSystemError)
  559. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  560. return
  561. }
  562. }
  563. code := FailCodeSystemError
  564. switch err {
  565. case ErrWrongPassword, ErrUserNotFound:
  566. code = FailCodeIncorrectAccountNameOrPassword
  567. case ErrUserLocked, ErrUserGetLocked:
  568. code = FailCodeUserLocked
  569. }
  570. setFailCodeFlash(w, code)
  571. setWrongLoginInputFlash(w, WrongLoginInputFlash{
  572. Account: account,
  573. Password: password,
  574. })
  575. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  576. return
  577. }
  578. u := user.(UserPasser)
  579. userID := objectID(user)
  580. claims := UserClaims{
  581. UserID: userID,
  582. PassUpdatedAt: u.GetPasswordUpdatedAt(),
  583. RegisteredClaims: b.genBaseSessionClaim(userID),
  584. }
  585. if !b.totpEnabled {
  586. if b.afterLoginHook != nil {
  587. setCookieForRequest(r, &http.Cookie{Name: b.authCookieName, Value: b.mustGetSessionToken(claims)})
  588. if herr := b.afterLoginHook(r, user); herr != nil {
  589. setFailCodeFlash(w, FailCodeSystemError)
  590. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  591. return
  592. }
  593. }
  594. }
  595. if err = b.setSecureCookiesByClaims(w, user, claims); err != nil {
  596. setFailCodeFlash(w, FailCodeSystemError)
  597. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  598. return
  599. }
  600. if b.totpEnabled {
  601. if u.GetIsTOTPSetup() {
  602. http.Redirect(w, r, b.totpValidatePageURL, http.StatusFound)
  603. return
  604. }
  605. var key *otp.Key
  606. if key, err = totp.Generate(
  607. totp.GenerateOpts{
  608. Issuer: b.totpIssuer,
  609. AccountName: u.GetAccountName(),
  610. },
  611. ); err != nil {
  612. setFailCodeFlash(w, FailCodeSystemError)
  613. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  614. return
  615. }
  616. if err = u.SetTOTPSecret(b.db, b.newUserObject(), key.Secret()); err != nil {
  617. setFailCodeFlash(w, FailCodeSystemError)
  618. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  619. return
  620. }
  621. http.Redirect(w, r, b.totpSetupPageURL, http.StatusFound)
  622. return
  623. }
  624. redirectURL := b.homePageURLFunc(r, user)
  625. if v := b.getContinueURL(w, r); v != "" {
  626. redirectURL = v
  627. }
  628. http.Redirect(w, r, redirectURL, http.StatusFound)
  629. return
  630. }
  631. func (b *Builder) genBaseSessionClaim(id string) jwt.RegisteredClaims {
  632. return genBaseClaims(id, b.sessionMaxAge)
  633. }
  634. func (b *Builder) mustGetSessionToken(claims UserClaims) string {
  635. return mustSignClaims(claims, b.secret)
  636. }
  637. func (b *Builder) setAuthCookiesFromUserClaims(w http.ResponseWriter, claims *UserClaims, secureSalt string) error {
  638. http.SetCookie(w, &http.Cookie{
  639. Name: b.authCookieName,
  640. Value: b.mustGetSessionToken(*claims),
  641. Path: b.cookieConfig.Path,
  642. Domain: b.cookieConfig.Domain,
  643. MaxAge: b.sessionMaxAge,
  644. Expires: time.Now().Add(time.Duration(b.sessionMaxAge) * time.Second),
  645. HttpOnly: true,
  646. Secure: true,
  647. SameSite: b.cookieConfig.SameSite,
  648. })
  649. if secureSalt != "" {
  650. http.SetCookie(w, &http.Cookie{
  651. Name: b.authSecureCookieName,
  652. Value: mustSignClaims(&claims.RegisteredClaims, b.secret+secureSalt),
  653. Path: b.cookieConfig.Path,
  654. Domain: b.cookieConfig.Domain,
  655. MaxAge: b.sessionMaxAge,
  656. Expires: time.Now().Add(time.Duration(b.sessionMaxAge) * time.Second),
  657. HttpOnly: true,
  658. Secure: true,
  659. SameSite: b.cookieConfig.SameSite,
  660. })
  661. }
  662. return nil
  663. }
  664. func (b *Builder) cleanAuthCookies(w http.ResponseWriter) {
  665. http.SetCookie(w, &http.Cookie{
  666. Name: b.authCookieName,
  667. Value: "",
  668. Path: b.cookieConfig.Path,
  669. Domain: b.cookieConfig.Domain,
  670. MaxAge: -1,
  671. Expires: time.Unix(1, 0),
  672. HttpOnly: true,
  673. Secure: true,
  674. })
  675. http.SetCookie(w, &http.Cookie{
  676. Name: b.authSecureCookieName,
  677. Value: "",
  678. Path: b.cookieConfig.Path,
  679. Domain: b.cookieConfig.Domain,
  680. MaxAge: -1,
  681. Expires: time.Unix(1, 0),
  682. HttpOnly: true,
  683. Secure: true,
  684. })
  685. }
  686. func (b *Builder) setContinueURL(w http.ResponseWriter, r *http.Request) {
  687. continueURL := r.RequestURI
  688. if strings.Contains(continueURL, "?__execute_event__=") {
  689. continueURL = r.Referer()
  690. }
  691. if !strings.HasPrefix(continueURL, "/auth/") {
  692. http.SetCookie(w, &http.Cookie{
  693. Name: b.continueUrlCookieName,
  694. Value: continueURL,
  695. Path: b.cookieConfig.Path,
  696. Domain: b.cookieConfig.Domain,
  697. HttpOnly: true,
  698. })
  699. }
  700. }
  701. func (b *Builder) getContinueURL(w http.ResponseWriter, r *http.Request) string {
  702. c, err := r.Cookie(b.continueUrlCookieName)
  703. if err != nil || c.Value == "" {
  704. return ""
  705. }
  706. http.SetCookie(w, &http.Cookie{
  707. Name: b.continueUrlCookieName,
  708. Value: "",
  709. MaxAge: -1,
  710. Expires: time.Unix(1, 0),
  711. Path: b.cookieConfig.Path,
  712. Domain: b.cookieConfig.Domain,
  713. HttpOnly: true,
  714. })
  715. return c.Value
  716. }
  717. func (b *Builder) setSecureCookiesByClaims(w http.ResponseWriter, user interface{}, claims UserClaims) (err error) {
  718. var secureSalt string
  719. if b.sessionSecureEnabled {
  720. if user.(SessionSecurer).GetSecure() == "" {
  721. err = user.(SessionSecurer).UpdateSecure(b.db, b.newUserObject(), objectID(user))
  722. if err != nil {
  723. return err
  724. }
  725. }
  726. secureSalt = user.(SessionSecurer).GetSecure()
  727. }
  728. if err = b.setAuthCookiesFromUserClaims(w, &claims, secureSalt); err != nil {
  729. return err
  730. }
  731. return nil
  732. }
  733. func (b *Builder) consumeTOTPCode(r *http.Request, up UserPasser, passcode string) error {
  734. if !totp.Validate(passcode, up.GetTOTPSecret()) {
  735. return ErrWrongTOTPCode
  736. }
  737. lastCode, usedAt := up.GetLastUsedTOTPCode()
  738. if usedAt != nil && time.Now().Sub(*usedAt) > 90*time.Second {
  739. lastCode = ""
  740. }
  741. if passcode == lastCode {
  742. if b.afterTOTPCodeReusedHook != nil {
  743. if herr := b.afterTOTPCodeReusedHook(r, GetCurrentUser(r)); herr != nil {
  744. return herr
  745. }
  746. }
  747. return ErrTOTPCodeHasBeenUsed
  748. }
  749. if err := up.SetLastUsedTOTPCode(b.db, b.newUserObject(), passcode); err != nil {
  750. return err
  751. }
  752. return nil
  753. }
  754. func (b *Builder) getFailCodeFromTOTPCodeConsumeError(verr error) FailCode {
  755. fc := FailCodeSystemError
  756. switch verr {
  757. case ErrWrongTOTPCode:
  758. fc = FailCodeIncorrectTOTPCode
  759. case ErrTOTPCodeHasBeenUsed:
  760. fc = FailCodeTOTPCodeHasBeenUsed
  761. }
  762. return fc
  763. }
  764. // logout is for url "/logout/{provider}"
  765. func (b *Builder) logout(w http.ResponseWriter, r *http.Request) {
  766. err := gothic.Logout(w, r)
  767. if err != nil {
  768. //
  769. }
  770. b.cleanAuthCookies(w)
  771. if b.afterLogoutHook != nil {
  772. user := GetCurrentUser(r)
  773. if user != nil {
  774. if herr := b.afterLogoutHook(r, user); herr != nil {
  775. setFailCodeFlash(w, FailCodeSystemError)
  776. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  777. return
  778. }
  779. }
  780. }
  781. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  782. }
  783. // beginAuth is for url "/auth/{provider}"
  784. func (b *Builder) beginAuth(w http.ResponseWriter, r *http.Request) {
  785. gothic.BeginAuthHandler(w, r)
  786. }
  787. func (b *Builder) sendResetPasswordLink(w http.ResponseWriter, r *http.Request) {
  788. if r.Method != http.MethodPost {
  789. w.WriteHeader(http.StatusNotFound)
  790. return
  791. }
  792. failRedirectURL := b.forgetPasswordPageURL
  793. // check reCAPTCHA token
  794. if b.recaptchaEnabled {
  795. token := r.FormValue("token")
  796. if !recaptchaTokenCheck(b, token) {
  797. setFailCodeFlash(w, FailCodeIncorrectRecaptchaToken)
  798. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  799. return
  800. }
  801. }
  802. account := strings.TrimSpace(r.FormValue("account"))
  803. passcode := r.FormValue("otp")
  804. doTOTP := r.URL.Query().Get("totp") == "1"
  805. if doTOTP {
  806. failRedirectURL = MustSetQuery(failRedirectURL, "totp", "1")
  807. }
  808. if account == "" {
  809. setFailCodeFlash(w, FailCodeAccountIsRequired)
  810. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  811. return
  812. }
  813. u, err := b.userModel.(UserPasser).FindUser(b.db, b.newUserObject(), account)
  814. if err != nil {
  815. if err == gorm.ErrRecordNotFound {
  816. setFailCodeFlash(w, FailCodeUserNotFound)
  817. } else {
  818. setFailCodeFlash(w, FailCodeSystemError)
  819. }
  820. setWrongForgetPasswordInputFlash(w, WrongForgetPasswordInputFlash{
  821. Account: account,
  822. })
  823. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  824. return
  825. }
  826. _, createdAt, _ := u.(UserPasser).GetResetPasswordToken()
  827. if createdAt != nil {
  828. v := 60 - int(time.Now().Sub(*createdAt).Seconds())
  829. if v > 0 {
  830. setSecondsToRedoFlash(w, v)
  831. setWrongForgetPasswordInputFlash(w, WrongForgetPasswordInputFlash{
  832. Account: account,
  833. })
  834. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  835. return
  836. }
  837. }
  838. if u.(UserPasser).GetIsTOTPSetup() {
  839. if !doTOTP {
  840. setWrongForgetPasswordInputFlash(w, WrongForgetPasswordInputFlash{
  841. Account: account,
  842. })
  843. failRedirectURL = MustSetQuery(failRedirectURL, "totp", "1")
  844. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  845. return
  846. }
  847. if err = b.consumeTOTPCode(r, u.(UserPasser), passcode); err != nil {
  848. fc := b.getFailCodeFromTOTPCodeConsumeError(err)
  849. setFailCodeFlash(w, fc)
  850. setWrongForgetPasswordInputFlash(w, WrongForgetPasswordInputFlash{
  851. Account: account,
  852. TOTP: passcode,
  853. })
  854. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  855. return
  856. }
  857. }
  858. token, err := u.(UserPasser).GenerateResetPasswordToken(b.db, b.newUserObject())
  859. if err != nil {
  860. setFailCodeFlash(w, FailCodeSystemError)
  861. setWrongForgetPasswordInputFlash(w, WrongForgetPasswordInputFlash{
  862. Account: account,
  863. })
  864. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  865. return
  866. }
  867. scheme := "https"
  868. if r.TLS == nil {
  869. scheme = "http"
  870. }
  871. link := fmt.Sprintf("%s://%s%s?id=%s&token=%s", scheme, r.Host, b.resetPasswordPageURL, objectID(u), token)
  872. if doTOTP {
  873. link = MustSetQuery(link, "totp", "1")
  874. }
  875. if err = b.notifyUserOfResetPasswordLinkFunc(u, link); err != nil {
  876. setFailCodeFlash(w, FailCodeSystemError)
  877. setWrongForgetPasswordInputFlash(w, WrongForgetPasswordInputFlash{
  878. Account: account,
  879. })
  880. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  881. return
  882. }
  883. if b.afterSendResetPasswordLinkHook != nil {
  884. if herr := b.afterSendResetPasswordLinkHook(r, u); herr != nil {
  885. setFailCodeFlash(w, FailCodeSystemError)
  886. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  887. return
  888. }
  889. }
  890. http.Redirect(w, r, fmt.Sprintf("%s?a=%s", b.resetPasswordLinkSentPageURL, account), http.StatusFound)
  891. return
  892. }
  893. func (b *Builder) doResetPassword(w http.ResponseWriter, r *http.Request) {
  894. if r.Method != http.MethodPost {
  895. w.WriteHeader(http.StatusNotFound)
  896. return
  897. }
  898. userID := r.FormValue("user_id")
  899. token := r.FormValue("token")
  900. passcode := r.FormValue("otp")
  901. doTOTP := r.URL.Query().Get("totp") == "1"
  902. failRedirectURL := fmt.Sprintf("%s?id=%s&token=%s", b.resetPasswordPageURL, userID, token)
  903. if doTOTP {
  904. failRedirectURL = MustSetQuery(failRedirectURL, "totp", "1")
  905. }
  906. if userID == "" {
  907. setFailCodeFlash(w, FailCodeUserNotFound)
  908. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  909. return
  910. }
  911. if token == "" {
  912. setFailCodeFlash(w, FailCodeInvalidToken)
  913. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  914. return
  915. }
  916. password := r.FormValue("password")
  917. confirmPassword := r.FormValue("confirm_password")
  918. if password == "" {
  919. setFailCodeFlash(w, FailCodePasswordCannotBeEmpty)
  920. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  921. return
  922. }
  923. if confirmPassword != password {
  924. setFailCodeFlash(w, FailCodePasswordNotMatch)
  925. setWrongResetPasswordInputFlash(w, WrongResetPasswordInputFlash{
  926. Password: password,
  927. ConfirmPassword: confirmPassword,
  928. })
  929. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  930. return
  931. }
  932. if b.passwordValidationFunc != nil {
  933. msg, ok := b.passwordValidationFunc(password)
  934. if !ok {
  935. setCustomErrorMessageFlash(w, msg)
  936. setWrongResetPasswordInputFlash(w, WrongResetPasswordInputFlash{
  937. Password: password,
  938. ConfirmPassword: confirmPassword,
  939. })
  940. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  941. return
  942. }
  943. }
  944. u, err := b.findUserByID(userID)
  945. if err != nil {
  946. if err == ErrUserNotFound {
  947. setFailCodeFlash(w, FailCodeUserNotFound)
  948. } else {
  949. setFailCodeFlash(w, FailCodeSystemError)
  950. }
  951. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  952. return
  953. }
  954. storedToken, _, expired := u.(UserPasser).GetResetPasswordToken()
  955. if expired {
  956. setFailCodeFlash(w, FailCodeTokenExpired)
  957. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  958. return
  959. }
  960. if token != storedToken {
  961. setFailCodeFlash(w, FailCodeInvalidToken)
  962. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  963. return
  964. }
  965. if u.(UserPasser).GetIsTOTPSetup() {
  966. if !doTOTP {
  967. setWrongResetPasswordInputFlash(w, WrongResetPasswordInputFlash{
  968. Password: password,
  969. ConfirmPassword: confirmPassword,
  970. })
  971. failRedirectURL = MustSetQuery(failRedirectURL, "totp", "1")
  972. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  973. return
  974. }
  975. if err = b.consumeTOTPCode(r, u.(UserPasser), passcode); err != nil {
  976. fc := b.getFailCodeFromTOTPCodeConsumeError(err)
  977. setFailCodeFlash(w, fc)
  978. setWrongResetPasswordInputFlash(w, WrongResetPasswordInputFlash{
  979. Password: password,
  980. ConfirmPassword: confirmPassword,
  981. TOTP: passcode,
  982. })
  983. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  984. return
  985. }
  986. }
  987. err = u.(UserPasser).ConsumeResetPasswordToken(b.db, b.newUserObject())
  988. if err != nil {
  989. setFailCodeFlash(w, FailCodeSystemError)
  990. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  991. return
  992. }
  993. err = u.(UserPasser).SetPassword(b.db, b.newUserObject(), password)
  994. if err != nil {
  995. setFailCodeFlash(w, FailCodeSystemError)
  996. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  997. return
  998. }
  999. if b.afterResetPasswordHook != nil {
  1000. if herr := b.afterResetPasswordHook(r, u); herr != nil {
  1001. setFailCodeFlash(w, FailCodeSystemError)
  1002. http.Redirect(w, r, failRedirectURL, http.StatusFound)
  1003. return
  1004. }
  1005. }
  1006. setInfoCodeFlash(w, InfoCodePasswordSuccessfullyReset)
  1007. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  1008. return
  1009. }
  1010. type ValidationError struct {
  1011. Msg string
  1012. }
  1013. func (e *ValidationError) Error() string {
  1014. return e.Msg
  1015. }
  1016. // validationError
  1017. // errWrongPassword
  1018. // errEmptyPassword
  1019. // errPasswordNotMatch
  1020. // errWrongTOTPCode
  1021. // errTOTPCodeHasBeenUsed
  1022. func (b *Builder) ChangePassword(
  1023. r *http.Request,
  1024. oldPassword string,
  1025. password string,
  1026. confirmPassword string,
  1027. otp string,
  1028. ) error {
  1029. user := GetCurrentUser(r).(UserPasser)
  1030. if !user.IsPasswordCorrect(oldPassword) {
  1031. return ErrWrongPassword
  1032. }
  1033. if password == "" {
  1034. return ErrEmptyPassword
  1035. }
  1036. if confirmPassword != password {
  1037. return ErrPasswordNotMatch
  1038. }
  1039. if b.passwordValidationFunc != nil {
  1040. msg, ok := b.passwordValidationFunc(password)
  1041. if !ok {
  1042. return &ValidationError{Msg: msg}
  1043. }
  1044. }
  1045. if b.totpEnabled {
  1046. if err := b.consumeTOTPCode(r, user, otp); err != nil {
  1047. return err
  1048. }
  1049. }
  1050. err := user.SetPassword(b.db, b.newUserObject(), password)
  1051. if err != nil {
  1052. return err
  1053. }
  1054. if b.afterChangePasswordHook != nil {
  1055. if herr := b.afterChangePasswordHook(r, user); herr != nil {
  1056. return herr
  1057. }
  1058. }
  1059. return nil
  1060. }
  1061. func (b *Builder) doFormChangePassword(w http.ResponseWriter, r *http.Request) {
  1062. if r.Method != http.MethodPost {
  1063. w.WriteHeader(http.StatusNotFound)
  1064. return
  1065. }
  1066. oldPassword := r.FormValue("old_password")
  1067. password := r.FormValue("password")
  1068. confirmPassword := r.FormValue("confirm_password")
  1069. otp := r.FormValue("otp")
  1070. redirectURL := b.changePasswordPageURL
  1071. err := b.ChangePassword(r, oldPassword, password, confirmPassword, otp)
  1072. if err != nil {
  1073. if ve, ok := err.(*ValidationError); ok {
  1074. setCustomErrorMessageFlash(w, ve.Msg)
  1075. } else {
  1076. fc := FailCodeSystemError
  1077. switch err {
  1078. case ErrWrongPassword:
  1079. fc = FailCodeIncorrectPassword
  1080. case ErrEmptyPassword:
  1081. fc = FailCodePasswordCannotBeEmpty
  1082. case ErrPasswordNotMatch:
  1083. fc = FailCodePasswordNotMatch
  1084. case ErrWrongTOTPCode:
  1085. fc = FailCodeIncorrectTOTPCode
  1086. case ErrTOTPCodeHasBeenUsed:
  1087. fc = FailCodeTOTPCodeHasBeenUsed
  1088. }
  1089. setFailCodeFlash(w, fc)
  1090. }
  1091. setWrongChangePasswordInputFlash(w, WrongChangePasswordInputFlash{
  1092. OldPassword: oldPassword,
  1093. NewPassword: password,
  1094. ConfirmPassword: confirmPassword,
  1095. TOTP: otp,
  1096. })
  1097. http.Redirect(w, r, redirectURL, http.StatusFound)
  1098. return
  1099. }
  1100. setInfoCodeFlash(w, InfoCodePasswordSuccessfullyChanged)
  1101. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  1102. }
  1103. func (b *Builder) totpDo(w http.ResponseWriter, r *http.Request) {
  1104. if r.Method != http.MethodPost {
  1105. w.WriteHeader(http.StatusNotFound)
  1106. return
  1107. }
  1108. var err error
  1109. var user interface{}
  1110. defer func() {
  1111. if b.afterFailedToLoginHook != nil && err != nil && user != nil {
  1112. b.afterFailedToLoginHook(r, user)
  1113. }
  1114. }()
  1115. var claims *UserClaims
  1116. claims, err = parseUserClaimsFromCookie(r, b.authCookieName, b.secret)
  1117. if err != nil {
  1118. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  1119. return
  1120. }
  1121. user, err = b.findUserByID(claims.UserID)
  1122. if err != nil {
  1123. if err == ErrUserNotFound {
  1124. setFailCodeFlash(w, FailCodeUserNotFound)
  1125. } else {
  1126. setFailCodeFlash(w, FailCodeSystemError)
  1127. }
  1128. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  1129. return
  1130. }
  1131. u := user.(UserPasser)
  1132. otp := r.FormValue("otp")
  1133. isTOTPSetup := u.GetIsTOTPSetup()
  1134. if err := b.consumeTOTPCode(r, u, otp); err != nil {
  1135. fc := b.getFailCodeFromTOTPCodeConsumeError(err)
  1136. setFailCodeFlash(w, fc)
  1137. redirectURL := b.totpValidatePageURL
  1138. if !isTOTPSetup {
  1139. redirectURL = b.totpSetupPageURL
  1140. }
  1141. http.Redirect(w, r, redirectURL, http.StatusFound)
  1142. return
  1143. }
  1144. if !isTOTPSetup {
  1145. if err = u.SetIsTOTPSetup(b.db, b.newUserObject(), true); err != nil {
  1146. setFailCodeFlash(w, FailCodeSystemError)
  1147. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  1148. return
  1149. }
  1150. }
  1151. claims.TOTPValidated = true
  1152. if b.afterLoginHook != nil {
  1153. setCookieForRequest(r, &http.Cookie{Name: b.authCookieName, Value: b.mustGetSessionToken(*claims)})
  1154. if herr := b.afterLoginHook(r, user); herr != nil {
  1155. setFailCodeFlash(w, FailCodeSystemError)
  1156. http.Redirect(w, r, b.loginPageURL, http.StatusFound)
  1157. return
  1158. }
  1159. }
  1160. err = b.setSecureCookiesByClaims(w, user, *claims)
  1161. if err != nil {
  1162. setFailCodeFlash(w, FailCodeSystemError)
  1163. http.Redirect(w, r, b.LogoutURL, http.StatusFound)
  1164. return
  1165. }
  1166. redirectURL := b.homePageURLFunc(r, user)
  1167. if v := b.getContinueURL(w, r); v != "" {
  1168. redirectURL = v
  1169. }
  1170. http.Redirect(w, r, redirectURL, http.StatusFound)
  1171. }
  1172. func (b *Builder) Mount(mux *http.ServeMux) {
  1173. if len(b.secret) == 0 {
  1174. panic("secret is empty")
  1175. }
  1176. if b.userModel != nil {
  1177. if b.db == nil {
  1178. panic("db is required")
  1179. }
  1180. }
  1181. wb := web.New()
  1182. mux.HandleFunc(b.LogoutURL, b.logout)
  1183. mux.Handle(b.loginPageURL, b.i18nBuilder.EnsureLanguage(wb.Page(b.loginPageFunc)))
  1184. if b.userPassEnabled {
  1185. mux.HandleFunc(b.passwordLoginURL, b.userpassLogin)
  1186. mux.HandleFunc(b.resetPasswordURL, b.doResetPassword)
  1187. mux.HandleFunc(b.changePasswordURL, b.doFormChangePassword)
  1188. mux.Handle(b.resetPasswordPageURL, b.i18nBuilder.EnsureLanguage(wb.Page(b.resetPasswordPageFunc)))
  1189. mux.Handle(b.changePasswordPageURL, b.i18nBuilder.EnsureLanguage(wb.Page(b.changePasswordPageFunc)))
  1190. if !b.noForgetPasswordLink {
  1191. mux.HandleFunc(b.sendResetPasswordLinkURL, b.sendResetPasswordLink)
  1192. mux.Handle(b.forgetPasswordPageURL, b.i18nBuilder.EnsureLanguage(wb.Page(b.forgetPasswordPageFunc)))
  1193. mux.Handle(b.resetPasswordLinkSentPageURL, b.i18nBuilder.EnsureLanguage(wb.Page(b.resetPasswordLinkSentPageFunc)))
  1194. }
  1195. if b.totpEnabled {
  1196. mux.HandleFunc(b.validateTOTPURL, b.totpDo)
  1197. mux.Handle(b.totpSetupPageURL, b.i18nBuilder.EnsureLanguage(wb.Page(b.totpSetupPageFunc)))
  1198. mux.Handle(b.totpValidatePageURL, b.i18nBuilder.EnsureLanguage(wb.Page(b.totpValidatePageFunc)))
  1199. }
  1200. }
  1201. if b.oauthEnabled {
  1202. mux.HandleFunc(b.oauthBeginURL, b.beginAuth)
  1203. mux.HandleFunc(b.oauthCallbackURL, b.completeUserAuthCallback)
  1204. mux.HandleFunc(b.oauthCallbackCompleteURL, b.completeUserAuthCallbackComplete)
  1205. }
  1206. // assets
  1207. assetsSubFS, err := fs.Sub(assetsFS, "assets")
  1208. if err != nil {
  1209. panic(err)
  1210. }
  1211. mux.Handle(assetsPathPrefix, http.StripPrefix(assetsPathPrefix, http.FileServer(http.FS(assetsSubFS))))
  1212. }