mirror of
https://github.com/writeas/writefreely
synced 2025-02-01 23:16:45 +01:00
Merge pull request #776 from writefreely/passwordless-login
Plumbing: login via emailed link Ref T731
This commit is contained in:
commit
7db4b699e2
49
account.go
49
account.go
@ -13,6 +13,7 @@ package writefreely
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/mailgun/mailgun-go"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -1237,6 +1238,54 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loginViaEmail(app *App, alias, redirectTo string) error {
|
||||||
|
if !app.cfg.Email.Enabled() {
|
||||||
|
return fmt.Errorf("EMAIL ISN'T CONFIGURED on this server")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure user has added an email
|
||||||
|
// TODO: create a new func to just get user's email; "ForAuth" doesn't match here
|
||||||
|
u, _ := app.db.GetUserForAuth(alias)
|
||||||
|
if u == nil {
|
||||||
|
if strings.IndexAny(alias, "@") > 0 {
|
||||||
|
return ErrUserNotFoundEmail
|
||||||
|
}
|
||||||
|
return ErrUserNotFound
|
||||||
|
}
|
||||||
|
if u.Email.String == "" {
|
||||||
|
return impart.HTTPError{http.StatusPreconditionFailed, "User doesn't have an email address. Log in with password, instead."}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate one-time login token
|
||||||
|
t, err := app.db.GetTemporaryOneTimeAccessToken(u.ID, 60*15, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to generate token for email login: %s", err)
|
||||||
|
return impart.HTTPError{http.StatusInternalServerError, "Unable to generate token."}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send email
|
||||||
|
gun := mailgun.NewMailgun(app.cfg.Email.Domain, app.cfg.Email.MailgunPrivate)
|
||||||
|
toEmail := u.EmailClear(app.keys)
|
||||||
|
footerPara := "This link will only work once and expires in 15 minutes. Didn't ask us to log in? You can safely ignore this email."
|
||||||
|
|
||||||
|
plainMsg := fmt.Sprintf("Log in to %s here: %s/login?to=%s&with=%s\n\n%s", app.cfg.App.SiteName, app.cfg.App.Host, redirectTo, t, footerPara)
|
||||||
|
m := mailgun.NewMessage(app.cfg.App.SiteName+" <noreply-login@"+app.cfg.Email.Domain+">", "Log in to "+app.cfg.App.SiteName, plainMsg, fmt.Sprintf("<%s>", toEmail))
|
||||||
|
m.AddTag("Email Login")
|
||||||
|
|
||||||
|
m.SetHtml(fmt.Sprintf(`<html>
|
||||||
|
<body style="font-family:Lora, 'Palatino Linotype', Palatino, Baskerville, 'Book Antiqua', 'New York', 'DejaVu serif', serif; font-size: 100%%; margin:1em 2em;">
|
||||||
|
<div style="margin:0 auto; max-width: 40em; font-size: 1.2em;">
|
||||||
|
<h1 style="font-size:1.75em"><a style="text-decoration:none;color:#000;" href="%s">%s</a></h1>
|
||||||
|
<p style="font-size:1.2em;margin-bottom:1.5em;text-align:center"><a href="%s/login?to=%s&with=%s">Log in to %s here</a>.</p>
|
||||||
|
<p style="font-size: 0.86em;color:#666;text-align:center;max-width:35em;margin:1em auto">%s</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`, app.cfg.App.Host, app.cfg.App.SiteName, app.cfg.App.Host, redirectTo, t, app.cfg.App.SiteName, footerPara))
|
||||||
|
_, _, err = gun.Send(m)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func saveTempInfo(app *App, key, val string, r *http.Request, w http.ResponseWriter) error {
|
func saveTempInfo(app *App, key, val string, r *http.Request, w http.ResponseWriter) error {
|
||||||
session, err := app.sessionStore.Get(r, "t")
|
session, err := app.sessionStore.Get(r, "t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -174,6 +174,13 @@ func (db *datastore) upsert(indexedCols ...string) string {
|
|||||||
return "ON DUPLICATE KEY UPDATE"
|
return "ON DUPLICATE KEY UPDATE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *datastore) dateAdd(l int, unit string) string {
|
||||||
|
if db.driverName == driverSQLite {
|
||||||
|
return fmt.Sprintf("DATETIME('now', '%d %s')", l, unit)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("DATE_ADD(NOW(), INTERVAL %d %s)", l, unit)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *datastore) dateSub(l int, unit string) string {
|
func (db *datastore) dateSub(l int, unit string) string {
|
||||||
if db.driverName == driverSQLite {
|
if db.driverName == driverSQLite {
|
||||||
return fmt.Sprintf("DATETIME('now', '-%d %s')", l, unit)
|
return fmt.Sprintf("DATETIME('now', '-%d %s')", l, unit)
|
||||||
@ -567,7 +574,7 @@ func (db *datastore) GetTemporaryOneTimeAccessToken(userID int64, validSecs int,
|
|||||||
|
|
||||||
expirationVal := "NULL"
|
expirationVal := "NULL"
|
||||||
if validSecs > 0 {
|
if validSecs > 0 {
|
||||||
expirationVal = fmt.Sprintf("DATE_ADD("+db.now()+", INTERVAL %d SECOND)", validSecs)
|
expirationVal = db.dateAdd(validSecs, "SECOND")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO accesstokens (token, user_id, one_time, expires) VALUES (?, ?, ?, "+expirationVal+")", string(binTok), userID, oneTime)
|
_, err = db.Exec("INSERT INTO accesstokens (token, user_id, one_time, expires) VALUES (?, ?, ?, "+expirationVal+")", string(binTok), userID, oneTime)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user