builder.go 35 KB

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