builder.go 35 KB

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