├── README.md
├── .eslintrc.json
├── next.config.js
├── database.db
├── public
├── favicon.ico
└── vercel.svg
├── lib
├── initialiseDB.js
└── auth.js
├── pages
├── _app.js
├── index.js
├── api
│ └── auth
│ │ └── [...nextauth].js
├── login.js
└── signup.js
├── styles
├── globals.css
├── Home.module.css
└── auth.module.css
├── package.json
├── .gitignore
├── migrations
└── 001-init.sql
└── LICENSE
/README.md:
--------------------------------------------------------------------------------
1 | # Next-Auth Credentials Demo
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | reactStrictMode: true,
3 | }
4 |
--------------------------------------------------------------------------------
/database.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekrresa/next-auth-credentials-demo/HEAD/database.db
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekrresa/next-auth-credentials-demo/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/lib/initialiseDB.js:
--------------------------------------------------------------------------------
1 | import sqlite3 from "sqlite3";
2 | import { open } from "sqlite";
3 |
4 | export async function initialiseDB() {
5 | const db = await open({
6 | driver: sqlite3.Database,
7 | filename: "database.db",
8 | });
9 |
10 | // await db.migrate()
11 |
12 | return db;
13 | }
14 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import { SessionProvider } from "next-auth/react";
2 | import "../styles/globals.css";
3 |
4 | export default function MyApp({ Component, pageProps }) {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin: 0;
5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7 | }
8 |
9 | a {
10 | color: inherit;
11 | text-decoration: none;
12 | }
13 |
14 | * {
15 | box-sizing: border-box;
16 | }
17 |
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 | justify-content: center;
6 | align-items: center;
7 | padding: 0 1rem;
8 | }
9 |
10 | .title {
11 | font-size: 1.4rem;
12 | }
13 |
14 | .button {
15 | background: #2e73cd;
16 | color: #fff;
17 | padding: 0.4rem 1rem;
18 | border-radius: 4px;
19 | border: 1px solid;
20 | cursor: pointer;
21 | }
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-auth-demo",
3 | "private": true,
4 | "scripts": {
5 | "dev": "next dev",
6 | "build": "next build",
7 | "start": "next start",
8 | "lint": "next lint"
9 | },
10 | "dependencies": {
11 | "next": "12.0.9",
12 | "next-auth": "^4.3.1",
13 | "react": "17.0.2",
14 | "react-dom": "17.0.2",
15 | "sqlite": "^4.0.23",
16 | "sqlite3": "^5.0.2"
17 | },
18 | "devDependencies": {
19 | "eslint": "8.8.0",
20 | "eslint-config-next": "12.0.9"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/migrations/001-init.sql:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- Up
3 | --------------------------------------------------------------------------------
4 |
5 | CREATE TABLE IF NOT EXISTS users (
6 | id INTEGER PRIMARY KEY AUTOINCREMENT,
7 | name VARCHAR(255) NOT NULL,
8 | email VARCHAR(255) NOT NULL,
9 | password VARCHAR(255) NOT NULL
10 | );
11 |
12 | --------------------------------------------------------------------------------
13 | -- Down
14 | --------------------------------------------------------------------------------
15 |
16 | DROP TABLE IF EXISTS users;
--------------------------------------------------------------------------------
/styles/auth.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 | justify-content: center;
6 | align-items: center;
7 | padding: 0 1rem;
8 | }
9 |
10 | .form {
11 | display: flex;
12 | flex-direction: column;
13 | width: 16rem;
14 | }
15 |
16 | .label {
17 | font-size: 0.9rem;
18 | margin-bottom: 3px;
19 | display: inline-block;
20 | }
21 |
22 | .input {
23 | margin-bottom: 1rem;
24 | padding: 0.35rem 0.4rem;
25 | font-size: 0.85rem;
26 | border-radius: 4px;
27 | border: 1px solid #606060;
28 | }
29 |
30 | .button {
31 | background: #2e73cd;
32 | color: #fff;
33 | padding: 0.6rem 0.4rem;
34 | border-radius: 4px;
35 | border: 1px solid;
36 | cursor: pointer;
37 | }
38 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { useRouter } from "next/router";
3 | import { signOut, useSession } from "next-auth/react";
4 | import styles from "../styles/Home.module.css";
5 |
6 | export default function Home() {
7 | const { data, status } = useSession();
8 | const router = useRouter();
9 |
10 | if (status === "loading") {
11 | /**
12 | * Session is being fetched
13 | */
14 | return
Loading...
;
15 | }
16 |
17 | if (status === "unauthenticated") {
18 | router.push("/login");
19 | }
20 |
21 | return (
22 |
23 |
Hello, {data?.user?.name}
24 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Ochuko Ekrresa
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/auth.js:
--------------------------------------------------------------------------------
1 | import { initialiseDB } from "./initialiseDB";
2 |
3 | export async function login(credentials) {
4 | const db = await initialiseDB();
5 | const result = await db.get(
6 | `SELECT name, email, password FROM users WHERE email = ?`,
7 | [credentials.email]
8 | );
9 |
10 | if (!result) throw new Error("Invalid credentials");
11 |
12 | if (result.password !== credentials.password)
13 | throw new Error("Invalid credentials");
14 |
15 | return { name: result.name, email: result.email };
16 | }
17 |
18 | export async function signup(credentials) {
19 | const db = await initialiseDB();
20 |
21 | const existingUser = await db.get(`SELECT 1 FROM users WHERE email = ?`, [
22 | credentials.email,
23 | ]);
24 |
25 | if (existingUser) throw new Error("Email is already in use");
26 |
27 | const result = await db.run(
28 | `INSERT INTO users (name, email, password) VALUES (?, ?, ?)`,
29 | [credentials.name, credentials.email, credentials.password]
30 | );
31 |
32 | if (!result.changes) throw new Error("An error occurred");
33 |
34 | return await db.get(`SELECT name, email FROM users WHERE id = ?`, [
35 | result.lastID,
36 | ]);
37 | }
38 |
--------------------------------------------------------------------------------
/pages/api/auth/[...nextauth].js:
--------------------------------------------------------------------------------
1 | import NextAuth from "next-auth";
2 | import CredentialsProvider from "next-auth/providers/credentials";
3 | import * as Auth from "../../../lib/auth";
4 |
5 | export default NextAuth({
6 | providers: [
7 | CredentialsProvider({
8 | id: "login",
9 | name: "Login",
10 | type: "credentials",
11 | async authorize(credentials) {
12 | try {
13 | return await Auth.login(credentials);
14 | } catch (error) {
15 | throw new Error(error.message);
16 | }
17 | },
18 | }),
19 | CredentialsProvider({
20 | id: "signup",
21 | name: "Signup",
22 | type: "credentials",
23 | async authorize(credentials) {
24 | try {
25 | return await Auth.signup(credentials);
26 | } catch (error) {
27 | throw new Error(error.message);
28 | }
29 | },
30 | }),
31 | ],
32 | callbacks: {
33 | async signIn({ user }) {
34 | // console.log({ user });
35 | if (user) return true;
36 |
37 | return false;
38 | },
39 | async session({ session }) {
40 | session.user.isLoggedIn = true;
41 | return session;
42 | },
43 | async jwt({ token, user }) {
44 | return token;
45 | },
46 | },
47 | // use env variable in production
48 | secret: "looselipssinkships",
49 | });
50 |
--------------------------------------------------------------------------------
/pages/login.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { useRouter } from "next/router";
3 | import { signIn } from "next-auth/react";
4 | import styles from "../styles/auth.module.css";
5 |
6 | export default function Login() {
7 | const router = useRouter();
8 | const formRef = React.useRef(null);
9 |
10 | const handleSubmit = async (e) => {
11 | e.preventDefault();
12 |
13 | const formData = new FormData(formRef.current);
14 | if (!formData.get("email") || !formData.get("password")) return;
15 |
16 | const response = await signIn("login", {
17 | email: formData.get("email"),
18 | password: formData.get("password"),
19 | redirect: false,
20 | });
21 |
22 | if (response.error) return;
23 |
24 | router.push("/");
25 | };
26 |
27 | return (
28 |
29 |
Login
30 |
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/pages/signup.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { useRouter } from "next/router";
3 | import { signIn } from "next-auth/react";
4 | import styles from "../styles/auth.module.css";
5 |
6 | export default function Signup() {
7 | const router = useRouter();
8 | const formRef = React.useRef(null);
9 | const handleSubmit = async (e) => {
10 | e.preventDefault();
11 |
12 | const formData = new FormData(formRef.current);
13 | if (
14 | !formData.get("email") ||
15 | !formData.get("password") ||
16 | !formData.get("name")
17 | )
18 | return;
19 |
20 | const response = await signIn("signup", {
21 | name: formData.get("name"),
22 | email: formData.get("email"),
23 | password: formData.get("password"),
24 | redirect: false,
25 | });
26 |
27 | if (response.error) return;
28 |
29 | router.push("/");
30 | };
31 |
32 | return (
33 |
34 |
Signup
35 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------