From 6bd01aaa4520cecf65878b0283aca198593a0c39 Mon Sep 17 00:00:00 2001 From: Dario Guarascio Date: Sat, 9 Oct 2021 11:47:20 +0200 Subject: [PATCH] Autologin via `?hash=xxx` parameter in url --- components/forms/LoginForm.js | 16 +++++++++++++++- lib/queries.js | 10 ++++++++++ pages/api/auth/hash.js | 32 ++++++++++++++++++++++++++++++++ pages/dashboard/[[...id]].js | 2 +- pages/login.js | 4 +++- pages/website/[...id].js | 2 +- prisma/schema.mysql.prisma | 1 + prisma/schema.postgresql.prisma | 1 + 8 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 pages/api/auth/hash.js diff --git a/components/forms/LoginForm.js b/components/forms/LoginForm.js index 8ac3a556..7f74c95a 100644 --- a/components/forms/LoginForm.js +++ b/components/forms/LoginForm.js @@ -27,11 +27,25 @@ const validate = ({ username, password }) => { return errors; }; -export default function LoginForm() { +export default function LoginForm({ hash = null }) { const post = usePost(); const router = useRouter(); const [message, setMessage] = useState(); + const handleAutologin = async ({ hash }) => { + const autologin = await post('/api/auth/hash', { + hash, + }); + + if (autologin.ok) { + return router.push('/'); + } + }; + + if (hash) { + handleAutologin({ hash }); + } + const handleSubmit = async ({ username, password }) => { const { ok, status, data } = await post('/api/auth/login', { username, diff --git a/lib/queries.js b/lib/queries.js index ff9d557f..834ec178 100644 --- a/lib/queries.js +++ b/lib/queries.js @@ -223,6 +223,16 @@ export async function getAccountById(user_id) { ); } +export async function getAccountByHash(hash) { + return runQuery( + prisma.account.findUnique({ + where: { + hash, + }, + }), + ); +} + export async function getAccountByUsername(username) { return runQuery( prisma.account.findUnique({ diff --git a/pages/api/auth/hash.js b/pages/api/auth/hash.js new file mode 100644 index 00000000..f1bfc39d --- /dev/null +++ b/pages/api/auth/hash.js @@ -0,0 +1,32 @@ +import { serialize } from 'cookie'; +import { createSecureToken } from 'lib/crypto'; +import { getAccountByHash } from 'lib/queries'; +import { AUTH_COOKIE_NAME } from 'lib/constants'; +import { ok, unauthorized, badRequest } from 'lib/response'; + +export default async (req, res) => { + const { hash } = req.body; + + if (!hash) { + return badRequest(res); + } + + const account = await getAccountByHash(hash); + + if (account) { + const { user_id, username, is_admin } = account; + const token = await createSecureToken({ user_id, username, is_admin }); + const cookie = serialize(AUTH_COOKIE_NAME, token, { + path: '/', + httpOnly: true, + sameSite: true, + maxAge: 60 * 60 * 24 * 365, + }); + + res.setHeader('Set-Cookie', [cookie]); + + return ok(res, { token }); + } + + return unauthorized(res); +}; diff --git a/pages/dashboard/[[...id]].js b/pages/dashboard/[[...id]].js index d3ce1301..66e92873 100644 --- a/pages/dashboard/[[...id]].js +++ b/pages/dashboard/[[...id]].js @@ -15,7 +15,7 @@ export default function DashboardPage() { } return ( - + ); diff --git a/pages/login.js b/pages/login.js index cf976eca..f7ec7a25 100644 --- a/pages/login.js +++ b/pages/login.js @@ -1,11 +1,13 @@ import React from 'react'; import Layout from 'components/layout/Layout'; import LoginForm from 'components/forms/LoginForm'; +import { useRouter } from 'next/router'; export default function LoginPage() { + const { query } = useRouter(); return ( - + ); } diff --git a/pages/website/[...id].js b/pages/website/[...id].js index 90f4f492..6a5fda7d 100644 --- a/pages/website/[...id].js +++ b/pages/website/[...id].js @@ -16,7 +16,7 @@ export default function DetailsPage() { const [websiteId] = id; return ( - + ); diff --git a/prisma/schema.mysql.prisma b/prisma/schema.mysql.prisma index 40969d66..0441ae6f 100644 --- a/prisma/schema.mysql.prisma +++ b/prisma/schema.mysql.prisma @@ -11,6 +11,7 @@ model account { user_id Int @id @default(autoincrement()) @db.UnsignedInt username String @unique @db.VarChar(255) password String @db.VarChar(60) + hash String @unique @db.VarChar(512) is_admin Boolean @default(false) created_at DateTime? @default(now()) @db.Timestamp(0) updated_at DateTime? @default(now()) @db.Timestamp(0) diff --git a/prisma/schema.postgresql.prisma b/prisma/schema.postgresql.prisma index d46e7dc1..e3fab8c8 100644 --- a/prisma/schema.postgresql.prisma +++ b/prisma/schema.postgresql.prisma @@ -11,6 +11,7 @@ model account { user_id Int @id @default(autoincrement()) username String @unique @db.VarChar(255) password String @db.VarChar(60) + hash String @unique @db.VarChar(512) is_admin Boolean @default(false) created_at DateTime? @default(now()) @db.Timestamptz(6) updated_at DateTime? @default(now()) @db.Timestamptz(6)