forked from mirrors/pronouns.cc
feat: add email/password login
This commit is contained in:
parent
12ed7fb5bb
commit
1cfe28cd59
17 changed files with 188 additions and 59 deletions
|
@ -55,7 +55,7 @@ func (db *DB) UserByEmail(ctx context.Context, email string) (u User, err error)
|
||||||
|
|
||||||
// EmailExists returns whether an email address already exists. It does not need to be comfirmed.
|
// EmailExists returns whether an email address already exists. It does not need to be comfirmed.
|
||||||
func (db *DB) EmailExists(ctx context.Context, email string) (exists bool, err error) {
|
func (db *DB) EmailExists(ctx context.Context, email string) (exists bool, err error) {
|
||||||
err = db.QueryRow(ctx, "select exists(SELECT * FROM user_emails WHERE email = $1)", email).Scan(&exists)
|
err = db.QueryRow(ctx, "select exists(SELECT * FROM user_emails WHERE email_address = $1)", email).Scan(&exists)
|
||||||
return exists, err
|
return exists, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (db *DB) Tokens(ctx context.Context, userID xid.ID) (ts []Token, err error)
|
||||||
const TokenExpiryTime = 3 * 30 * 24 * time.Hour
|
const TokenExpiryTime = 3 * 30 * 24 * time.Hour
|
||||||
|
|
||||||
// SaveToken saves a token to the database.
|
// SaveToken saves a token to the database.
|
||||||
func (db *DB) SaveToken(ctx context.Context, userID xid.ID, tokenID xid.ID, apiOnly, readOnly bool) (t Token, err error) {
|
func (db *DB) SaveToken(ctx context.Context, q pgxscan.Querier, userID xid.ID, tokenID xid.ID, apiOnly, readOnly bool) (t Token, err error) {
|
||||||
sql, args, err := sq.Insert("tokens").
|
sql, args, err := sq.Insert("tokens").
|
||||||
SetMap(map[string]any{
|
SetMap(map[string]any{
|
||||||
"user_id": userID,
|
"user_id": userID,
|
||||||
|
@ -79,7 +79,7 @@ func (db *DB) SaveToken(ctx context.Context, userID xid.ID, tokenID xid.ID, apiO
|
||||||
return t, errors.Wrap(err, "building sql")
|
return t, errors.Wrap(err, "building sql")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pgxscan.Get(ctx, db, &t, sql, args...)
|
err = pgxscan.Get(ctx, q, &t, sql, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return t, errors.Wrap(err, "inserting token")
|
return t, errors.Wrap(err, "inserting token")
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,9 +142,7 @@ func (u User) VerifyPassword(input string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
inputHash := hashPassword([]byte(input), u.Salt)
|
inputHash := hashPassword([]byte(input), u.Salt)
|
||||||
verifiedHash := hashPassword(u.Password, u.Salt)
|
return subtle.ConstantTimeCompare(inputHash, u.Password) == 1
|
||||||
|
|
||||||
return subtle.ConstantTimeCompare(inputHash, verifiedHash) == 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashPassword(password, salt []byte) []byte {
|
func hashPassword(password, salt []byte) []byte {
|
||||||
|
|
|
@ -120,7 +120,7 @@ func (s *Server) discordCallback(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, s.DB, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
@ -361,12 +361,6 @@ func (s *Server) discordSignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
return errors.Wrap(err, "deleting signup ticket")
|
return errors.Wrap(err, "deleting signup ticket")
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit transaction
|
|
||||||
err = tx.Commit(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "committing transaction")
|
|
||||||
}
|
|
||||||
|
|
||||||
// create token
|
// create token
|
||||||
// TODO: implement user + token permissions
|
// TODO: implement user + token permissions
|
||||||
tokenID := xid.New()
|
tokenID := xid.New()
|
||||||
|
@ -376,11 +370,17 @@ func (s *Server) discordSignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, tx, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commit transaction
|
||||||
|
err = tx.Commit(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "committing transaction")
|
||||||
|
}
|
||||||
|
|
||||||
// return user
|
// return user
|
||||||
render.JSON(w, r, signupResponse{
|
render.JSON(w, r, signupResponse{
|
||||||
User: *dbUserToUserResponse(u, nil),
|
User: *dbUserToUserResponse(u, nil),
|
||||||
|
|
|
@ -141,7 +141,7 @@ func (s *Server) mastodonCallback(w http.ResponseWriter, r *http.Request) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, s.DB, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
@ -389,12 +389,6 @@ func (s *Server) mastodonSignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
return errors.Wrap(err, "deleting signup ticket")
|
return errors.Wrap(err, "deleting signup ticket")
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit transaction
|
|
||||||
err = tx.Commit(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "committing transaction")
|
|
||||||
}
|
|
||||||
|
|
||||||
// create token
|
// create token
|
||||||
// TODO: implement user + token permissions
|
// TODO: implement user + token permissions
|
||||||
tokenID := xid.New()
|
tokenID := xid.New()
|
||||||
|
@ -404,11 +398,17 @@ func (s *Server) mastodonSignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, tx, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commit transaction
|
||||||
|
err = tx.Commit(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "committing transaction")
|
||||||
|
}
|
||||||
|
|
||||||
// return user
|
// return user
|
||||||
render.JSON(w, r, signupResponse{
|
render.JSON(w, r, signupResponse{
|
||||||
User: *dbUserToUserResponse(u, nil),
|
User: *dbUserToUserResponse(u, nil),
|
||||||
|
|
|
@ -120,7 +120,7 @@ func (s *Server) misskeyCallback(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, s.DB, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
@ -317,12 +317,6 @@ func (s *Server) misskeySignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
return errors.Wrap(err, "deleting signup ticket")
|
return errors.Wrap(err, "deleting signup ticket")
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit transaction
|
|
||||||
err = tx.Commit(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "committing transaction")
|
|
||||||
}
|
|
||||||
|
|
||||||
// create token
|
// create token
|
||||||
// TODO: implement user + token permissions
|
// TODO: implement user + token permissions
|
||||||
tokenID := xid.New()
|
tokenID := xid.New()
|
||||||
|
@ -332,11 +326,17 @@ func (s *Server) misskeySignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, tx, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commit transaction
|
||||||
|
err = tx.Commit(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "committing transaction")
|
||||||
|
}
|
||||||
|
|
||||||
// return user
|
// return user
|
||||||
render.JSON(w, r, signupResponse{
|
render.JSON(w, r, signupResponse{
|
||||||
User: *dbUserToUserResponse(u, nil),
|
User: *dbUserToUserResponse(u, nil),
|
||||||
|
|
|
@ -139,7 +139,7 @@ func (s *Server) googleCallback(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, s.DB, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
@ -364,12 +364,6 @@ func (s *Server) googleSignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
return errors.Wrap(err, "deleting signup ticket")
|
return errors.Wrap(err, "deleting signup ticket")
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit transaction
|
|
||||||
err = tx.Commit(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "committing transaction")
|
|
||||||
}
|
|
||||||
|
|
||||||
// create token
|
// create token
|
||||||
// TODO: implement user + token permissions
|
// TODO: implement user + token permissions
|
||||||
tokenID := xid.New()
|
tokenID := xid.New()
|
||||||
|
@ -379,11 +373,17 @@ func (s *Server) googleSignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, tx, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commit transaction
|
||||||
|
err = tx.Commit(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "committing transaction")
|
||||||
|
}
|
||||||
|
|
||||||
// return user
|
// return user
|
||||||
render.JSON(w, r, signupResponse{
|
render.JSON(w, r, signupResponse{
|
||||||
User: *dbUserToUserResponse(u, nil),
|
User: *dbUserToUserResponse(u, nil),
|
||||||
|
|
|
@ -115,7 +115,7 @@ func (s *Server) createToken(w http.ResponseWriter, r *http.Request) error {
|
||||||
return errors.Wrap(err, "creating token")
|
return errors.Wrap(err, "creating token")
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := s.DB.SaveToken(ctx, claims.UserID, tokenID, true, readOnly)
|
t, err := s.DB.SaveToken(ctx, s.DB, claims.UserID, tokenID, true, readOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token")
|
return errors.Wrap(err, "saving token")
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ func (s *Server) tumblrCallback(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, s.DB, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
@ -397,12 +397,6 @@ func (s *Server) tumblrSignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
return errors.Wrap(err, "deleting signup ticket")
|
return errors.Wrap(err, "deleting signup ticket")
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit transaction
|
|
||||||
err = tx.Commit(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "committing transaction")
|
|
||||||
}
|
|
||||||
|
|
||||||
// create token
|
// create token
|
||||||
// TODO: implement user + token permissions
|
// TODO: implement user + token permissions
|
||||||
tokenID := xid.New()
|
tokenID := xid.New()
|
||||||
|
@ -412,11 +406,17 @@ func (s *Server) tumblrSignup(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, s.DB, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commit transaction
|
||||||
|
err = tx.Commit(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "committing transaction")
|
||||||
|
}
|
||||||
|
|
||||||
// return user
|
// return user
|
||||||
render.JSON(w, r, signupResponse{
|
render.JSON(w, r, signupResponse{
|
||||||
User: *dbUserToUserResponse(u, nil),
|
User: *dbUserToUserResponse(u, nil),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -72,10 +73,14 @@ func (s *Server) postEmailSignupConfirm(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
var email string
|
var email string
|
||||||
err = s.DB.Redis.Do(ctx, radix.Cmd(&email, "GET", emailSignupTicketKey(email)))
|
fmt.Println(emailSignupTicketKey(req.Ticket))
|
||||||
|
err = s.DB.Redis.Do(ctx, radix.Cmd(&email, "GET", emailSignupTicketKey(req.Ticket)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "getting email signup key")
|
return errors.Wrap(err, "getting email signup key")
|
||||||
}
|
}
|
||||||
|
if email == "" {
|
||||||
|
return server.APIError{Code: server.ErrBadRequest, Details: "Unknown ticket"}
|
||||||
|
}
|
||||||
|
|
||||||
tx, err := s.DB.Begin(ctx)
|
tx, err := s.DB.Begin(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -125,7 +130,7 @@ func (s *Server) postEmailSignupConfirm(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// save token to database
|
// save token to database
|
||||||
_, err = s.DB.SaveToken(ctx, u.ID, tokenID, false, false)
|
_, err = s.DB.SaveToken(ctx, tx, u.ID, tokenID, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "saving token to database")
|
return errors.Wrap(err, "saving token to database")
|
||||||
}
|
}
|
||||||
|
|
62
backend/routes/v2/auth/login.go
Normal file
62
backend/routes/v2/auth/login.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"codeberg.org/pronounscc/pronouns.cc/backend/server"
|
||||||
|
"emperror.dev/errors"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
"github.com/rs/xid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type postLoginRequest struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type postLoginResponse struct {
|
||||||
|
User *userResponse `json:"user,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postLogin(w http.ResponseWriter, r *http.Request) (err error) {
|
||||||
|
ctx := r.Context()
|
||||||
|
var req postLoginRequest
|
||||||
|
err = render.Decode(r, &req)
|
||||||
|
if err != nil {
|
||||||
|
return server.APIError{Code: server.ErrBadRequest}
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := s.DB.UserByEmail(ctx, req.Email)
|
||||||
|
if err != nil {
|
||||||
|
return server.APIError{Code: server.ErrForbidden, Details: "Invalid email or password"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !u.VerifyPassword(req.Password) {
|
||||||
|
return server.APIError{Code: server.ErrForbidden, Details: "Invalid email or password"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenID := xid.New()
|
||||||
|
token, err := s.Auth.CreateToken(u.ID, tokenID, u.IsAdmin, false, true)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "creating token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// save token to database
|
||||||
|
_, err = s.DB.SaveToken(ctx, s.DB, u.ID, tokenID, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "saving token to database")
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := s.DB.UserFields(ctx, u.ID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "querying fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, r, postLoginResponse{
|
||||||
|
Token: token,
|
||||||
|
User: dbUserToUserResponse(u, fields),
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ func Mount(srv *server.Server, r chi.Router) {
|
||||||
r.With(server.MustAuth).Post("/", nil) // Add/update email to existing account, { email }
|
r.With(server.MustAuth).Post("/", nil) // Add/update email to existing account, { email }
|
||||||
r.With(server.MustAuth).Delete("/{id}", nil) // Remove existing email from account, <no body>
|
r.With(server.MustAuth).Delete("/{id}", nil) // Remove existing email from account, <no body>
|
||||||
|
|
||||||
r.Post("/login", nil) // Log in to account, { username, password }
|
r.Post("/login", server.WrapHandler(s.postLogin)) // Log in to account, { username, password }
|
||||||
r.Post("/signup", server.WrapHandler(s.postEmailSignup)) // Create account, { email }
|
r.Post("/signup", server.WrapHandler(s.postEmailSignup)) // Create account, { email }
|
||||||
r.Post("/signup/confirm", server.WrapHandler(s.postEmailSignupConfirm)) // Create account, { ticket, username, password }
|
r.Post("/signup/confirm", server.WrapHandler(s.postEmailSignupConfirm)) // Create account, { ticket, username, password }
|
||||||
r.Post("/confirm", nil) // Confirm email address, { ticket }
|
r.Post("/confirm", nil) // Confirm email address, { ticket }
|
||||||
|
|
|
@ -40,12 +40,12 @@ func (s *Server) Template(template string, data map[string]any) (text, html []by
|
||||||
textWriter := new(bytes.Buffer)
|
textWriter := new(bytes.Buffer)
|
||||||
htmlWriter := new(bytes.Buffer)
|
htmlWriter := new(bytes.Buffer)
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(textWriter, "templates/"+template+".txt", data)
|
err = tmpl.ExecuteTemplate(textWriter, template+".txt", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrap(err, "executing text template")
|
return nil, nil, errors.Wrap(err, "executing text template")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(htmlWriter, "templates/"+template+".html", data)
|
err = tmpl.ExecuteTemplate(htmlWriter, template+".html", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrap(err, "executing HTML template")
|
return nil, nil, errors.Wrap(err, "executing HTML template")
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ export interface MetaResponse {
|
||||||
users: MetaUsers;
|
users: MetaUsers;
|
||||||
members: number;
|
members: number;
|
||||||
require_invite: boolean;
|
require_invite: boolean;
|
||||||
|
email_signup: boolean;
|
||||||
notice: { id: number; notice: string } | null;
|
notice: { id: number; notice: string } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { apiFetch } from "$lib/api/fetch";
|
import { apiFetch } from "$lib/api/fetch";
|
||||||
import { PUBLIC_BASE_URL } from "$env/static/public";
|
import { PUBLIC_BASE_URL } from "$env/static/public";
|
||||||
import type { UrlsResponse } from "$lib/api/responses";
|
import type { UrlsResponse } from "$lib/api/responses";
|
||||||
|
import type { APIError, MeUser } from "$lib/api/entities";
|
||||||
|
|
||||||
export const load = async () => {
|
export const load = async () => {
|
||||||
const resp = await apiFetch<UrlsResponse>("/auth/urls", {
|
const resp = await apiFetch<UrlsResponse>("/auth/urls", {
|
||||||
|
@ -12,3 +13,29 @@ export const load = async () => {
|
||||||
|
|
||||||
return resp;
|
return resp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface LoginResponse {
|
||||||
|
user?: MeUser;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
default: async ({ request }) => {
|
||||||
|
const data = await request.formData();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await apiFetch<LoginResponse>("/auth/email/login", {
|
||||||
|
method: "POST",
|
||||||
|
version: 2,
|
||||||
|
body: {
|
||||||
|
email: data.get("email"),
|
||||||
|
password: data.get("password"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data: resp };
|
||||||
|
} catch (e) {
|
||||||
|
return { error: e as APIError };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { enhance } from "$app/forms";
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import type { APIError } from "$lib/api/entities";
|
import type { APIError } from "$lib/api/entities";
|
||||||
import { apiFetch } from "$lib/api/fetch";
|
import { apiFetch } from "$lib/api/fetch";
|
||||||
|
@ -8,6 +9,7 @@
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
FormGroup,
|
||||||
Icon,
|
Icon,
|
||||||
Input,
|
Input,
|
||||||
ListGroup,
|
ListGroup,
|
||||||
|
@ -16,9 +18,21 @@
|
||||||
ModalBody,
|
ModalBody,
|
||||||
ModalFooter,
|
ModalFooter,
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData, ActionData } from "./$types";
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
export let form: ActionData;
|
||||||
|
|
||||||
|
$: loginCallback(form);
|
||||||
|
const loginCallback = (form: ActionData) => {
|
||||||
|
if (!form?.data?.user) return;
|
||||||
|
|
||||||
|
localStorage.setItem("pronouns-token", form.data.token);
|
||||||
|
localStorage.setItem("pronouns-user", JSON.stringify(form.data.user));
|
||||||
|
userStore.set(form.data.user);
|
||||||
|
addToast({ header: "Logged in", body: "Successfully logged in!" });
|
||||||
|
goto(`/@${form.data.user.name}`);
|
||||||
|
};
|
||||||
|
|
||||||
let error: APIError | null = null;
|
let error: APIError | null = null;
|
||||||
let instance = "";
|
let instance = "";
|
||||||
|
@ -59,7 +73,31 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Log in or sign up</h1>
|
<h1>Log in or sign up</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4 mb-1">
|
<div class="col-md">
|
||||||
|
{#if data.email_signup}
|
||||||
|
{#if form?.error}
|
||||||
|
<ErrorAlert error={form?.error} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<form method="POST" use:enhance>
|
||||||
|
<FormGroup class="m-1" floating label="Email">
|
||||||
|
<Input name="email" type="email" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup class="m-1" floating label="Password">
|
||||||
|
<Input name="password" type="password" />
|
||||||
|
</FormGroup>
|
||||||
|
<p class="m-1">
|
||||||
|
<Button color="primary" type="submit">Log in</Button>
|
||||||
|
<Button href="/auth/signup">Sign up</Button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
{:else}
|
||||||
|
<p>
|
||||||
|
<b>Choose an authentication provider to get started.</b> You can add more providers later.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="col-md">
|
||||||
<ListGroup>
|
<ListGroup>
|
||||||
<ListGroupItem tag="button" on:click={toggleModal}>Log in with the Fediverse</ListGroupItem>
|
<ListGroupItem tag="button" on:click={toggleModal}>Log in with the Fediverse</ListGroupItem>
|
||||||
{#if data.discord}
|
{#if data.discord}
|
||||||
|
@ -101,10 +139,5 @@
|
||||||
a token in your browser to identify your account.
|
a token in your browser to identify your account.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md">
|
|
||||||
<p>
|
|
||||||
<b>Choose an authentication provider to get started.</b> You can add more providers later.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,9 @@ const config = {
|
||||||
version: {
|
version: {
|
||||||
name: child_process.execSync("git describe --tags --long --always").toString().trim(),
|
name: child_process.execSync("git describe --tags --long --always").toString().trim(),
|
||||||
},
|
},
|
||||||
|
csrf: {
|
||||||
|
checkOrigin: process.env.NODE_ENV !== "development",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue