mirror of
https://github.com/KeyZox71/knl_meowscendence.git
synced 2025-08-16 13:32:53 +02:00
「✨」 feat: added google auth
This commit is contained in:
@ -3,7 +3,10 @@ import fastifyCookie from '@fastify/cookie';
|
||||
|
||||
import { register } from './register.js';
|
||||
import { login } from './login.js';
|
||||
import { gRedir } from './gRedir.js';
|
||||
import authDB from '../../utils/authDB.js'
|
||||
import { gLogCallback } from './gLogCallback.js';
|
||||
import { gRegisterCallback } from './gRegisterCallback.js';
|
||||
|
||||
const saltRounds = 10;
|
||||
|
||||
@ -36,7 +39,18 @@ export default async function(fastify, options) {
|
||||
});
|
||||
|
||||
// GOOGLE sign in
|
||||
|
||||
fastify.get('/login/google', async (request, reply) => {
|
||||
return gRedir(request, reply, fastify, '/login/google/callback');
|
||||
});
|
||||
fastify.get('/register/google', async (request, reply) => {
|
||||
return gRedir(request, reply, fastify, '/register/google/callback');
|
||||
});
|
||||
fastify.get('/login/google/callback', async (request, reply) => {
|
||||
return gLogCallback(request, reply, fastify);
|
||||
})
|
||||
fastify.get('/register/google/callback', async (request, reply) => {
|
||||
return gRegisterCallback(request, reply, fastify);
|
||||
})
|
||||
|
||||
fastify.post('/login', {
|
||||
schema: {
|
||||
|
55
src/api/auth/gLogCallback.js
Normal file
55
src/api/auth/gLogCallback.js
Normal file
@ -0,0 +1,55 @@
|
||||
import axios from 'axios'
|
||||
import authDB from '../../utils/authDB.js';
|
||||
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
/**
|
||||
* @param {import("fastify").FastifyRequest} request
|
||||
* @param {import("fastify").FastifyReply} reply
|
||||
* @param {import("fastify").FastifyInstance} fastify
|
||||
*
|
||||
* @returns {import('fastify').FastifyReply}
|
||||
*/
|
||||
export async function gLogCallback(request, reply, fastify) {
|
||||
const { code } = request.query;
|
||||
|
||||
try {
|
||||
const response = await axios.post('https://oauth2.googleapis.com/token', {
|
||||
code,
|
||||
client_id: process.env.GOOGLE_CLIENT_ID,
|
||||
client_secret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
redirect_uri: process.env.GOOGLE_CALLBACK_URL + '/login/google/callback',
|
||||
grant_type: 'authorization_code',
|
||||
});
|
||||
|
||||
const { access_token } = response.data;
|
||||
|
||||
const userInfoResponse = await axios.get('https://www.googleapis.com/oauth2/v3/userinfo', {
|
||||
headers: { Authorization: `Bearer ${access_token}` },
|
||||
});
|
||||
|
||||
const userProfile = userInfoResponse.data;
|
||||
const user = {
|
||||
username: userProfile.email, // Assuming email is used as the username
|
||||
};
|
||||
|
||||
if (!authDB.checkUser(user.username) || authDB.RESERVED_USERNAMES.includes(user.username)) {
|
||||
return reply.code(400).send({ error: "User does not exist" });
|
||||
}
|
||||
|
||||
const token = fastify.jwt.sign(user);
|
||||
|
||||
return reply
|
||||
.setCookie('token', token, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
secure: env !== 'development',
|
||||
sameSite: 'lax',
|
||||
})
|
||||
.code(200)
|
||||
.send({ msg: "Login successful" });
|
||||
} catch (error) {
|
||||
fastify.log.error(error);
|
||||
reply.code(500).send({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
23
src/api/auth/gRedir.js
Normal file
23
src/api/auth/gRedir.js
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @param {import("fastify").FastifyRequest} request
|
||||
* @param {import("fastify").FastifyReply} reply
|
||||
* @param {import("fastify").FastifyInstance} fastify
|
||||
* @param {string} callbackRoute
|
||||
*
|
||||
* @returns {import('fastify').FastifyReply}
|
||||
*/
|
||||
export async function gRedir(request, reply, fastify, callbackRoute) {
|
||||
try {
|
||||
const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
|
||||
`client_id=${process.env.GOOGLE_CLIENT_ID}&` +
|
||||
`redirect_uri=${encodeURIComponent(process.env.GOOGLE_CALLBACK_URL + callbackRoute)}&` +
|
||||
`response_type=code&` +
|
||||
`scope=email profile&` +
|
||||
`access_type=offline`;
|
||||
|
||||
return reply.redirect(authUrl);
|
||||
} catch (error) {
|
||||
fastify.log.error(error);
|
||||
return reply.code(500).send({ error: "Internal server error" });
|
||||
}
|
||||
}
|
64
src/api/auth/gRegisterCallback.js
Normal file
64
src/api/auth/gRegisterCallback.js
Normal file
@ -0,0 +1,64 @@
|
||||
import axios from 'axios'
|
||||
|
||||
import authDB from '../../utils/authDB.js';
|
||||
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
/**
|
||||
* @param {import("fastify").FastifyRequest} request
|
||||
* @param {import("fastify").FastifyReply} reply
|
||||
* @param {import("fastify").FastifyInstance} fastify
|
||||
*
|
||||
* @returns {import('fastify').FastifyReply}
|
||||
*/
|
||||
export async function gRegisterCallback(request, reply, fastify) {
|
||||
const { code } = request.query;
|
||||
|
||||
try {
|
||||
const response = await axios.post('https://oauth2.googleapis.com/token', {
|
||||
code,
|
||||
client_id: process.env.GOOGLE_CLIENT_ID,
|
||||
client_secret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
redirect_uri: process.env.GOOGLE_CALLBACK_URL + '/register/google/callback',
|
||||
grant_type: 'authorization_code',
|
||||
}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
});
|
||||
|
||||
const { access_token } = response.data;
|
||||
|
||||
const userInfoResponse = await axios.get('https://www.googleapis.com/oauth2/v3/userinfo', {
|
||||
headers: { Authorization: `Bearer ${access_token}` },
|
||||
});
|
||||
const userProfile = userInfoResponse.data;
|
||||
const user = {
|
||||
username: userProfile.email, // Assuming email is used as the username
|
||||
};
|
||||
|
||||
if (authDB.RESERVED_USERNAMES.includes(user)) {
|
||||
return reply.code(400).send({ error: 'Reserved username' });
|
||||
}
|
||||
if (authDB.checkUser(user.username) === true) {
|
||||
return reply.code(400).send({ error: "User already exist" });
|
||||
}
|
||||
|
||||
authDB.addUser(user.username, '');
|
||||
|
||||
const token = fastify.jwt.sign(user);
|
||||
|
||||
return reply
|
||||
.setCookie('token', token, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
secure: env !== 'development',
|
||||
sameSite: 'lax',
|
||||
})
|
||||
.code(200)
|
||||
.send({ msg: "Register successful" });
|
||||
} catch (error) {
|
||||
fastify.log.error(error);
|
||||
reply.code(500).send({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
import { checkUser } from '../../utils/authUtils.js';
|
||||
import authDB from '../../utils/authDB.js';
|
||||
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
@ -18,11 +17,11 @@ export async function login(request, reply, fastify) {
|
||||
/** @type {{ user: string, password: string }} */
|
||||
const { user, password } = request.body;
|
||||
|
||||
if (!checkUser(user) || user === 'admin') {
|
||||
if (!authDB.checkUser(user) || authDB.RESERVED_USERNAMES.includes(user)) {
|
||||
return reply.code(400).send({ error: "User does not exist" });
|
||||
}
|
||||
|
||||
const query = authDB.passwordQuery.get(user);
|
||||
const query = authDB.passwordQuery(user);
|
||||
const hash = query?.passwordHash;
|
||||
|
||||
if (!hash) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
import { isValidString, checkUser } from '../../utils/authUtils.js';
|
||||
import { isValidString } from '../../utils/authUtils.js';
|
||||
import authDB from '../../utils/authDB.js';
|
||||
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
@ -25,7 +25,7 @@ export async function register(request, reply, saltRounds, fastify) {
|
||||
|
||||
if (!isValidString(user) || !isValidString(password)) {
|
||||
return reply.code(400).send({ error: 'Invalid username or password' });
|
||||
} else if (checkUser(user) === true) {
|
||||
} else if (authDB.checkUser(user) === true) {
|
||||
return reply.code(400).send({ error: "User already exist" });
|
||||
} else if (password.length <= 8) {
|
||||
return reply.code(400).send({ error: "Password too short" });
|
||||
@ -34,7 +34,7 @@ export async function register(request, reply, saltRounds, fastify) {
|
||||
}
|
||||
|
||||
const hash = await bcrypt.hash(password, saltRounds);
|
||||
authDB.userAdd.run(user, hash);
|
||||
authDB.addUser(user, hash);
|
||||
|
||||
const token = fastify.jwt.sign({ user });
|
||||
|
||||
|
@ -3,7 +3,6 @@ import Database from 'better-sqlite3';
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
let database;
|
||||
const RESERVED_USERNAMES = ['admin'];
|
||||
let userCheck, passwordQuery, userAdd;
|
||||
|
||||
if (!env || env === 'development') {
|
||||
database = new Database(":memory:", { verbose: console.log });
|
||||
@ -22,17 +21,39 @@ function prepareDB() {
|
||||
passwordHash TEXT
|
||||
) STRICT
|
||||
`);
|
||||
userCheck = database.prepare('SELECT EXISTS (SELECT 1 FROM credentials WHERE username = ?);');
|
||||
passwordQuery = database.prepare('SELECT passwordHash FROM credentials WHERE username = ?;');
|
||||
userAdd = database.prepare('INSERT INTO credentials (username, passwordHash) VALUES (?, ?)');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function checkUser(name) {
|
||||
/**
|
||||
* @type: {import('better-sqlite3').Statement}
|
||||
*/
|
||||
let userCheck = database.prepare('SELECT EXISTS (SELECT 1 FROM credentials WHERE username = ?);');
|
||||
const result = userCheck.get(name);
|
||||
const key = Object.keys(result)[0];
|
||||
|
||||
return result[key] === 1;
|
||||
}
|
||||
|
||||
function addUser(name, pass) {
|
||||
let userAdd = database.prepare('INSERT INTO credentials (username, passwordHash) VALUES (?, ?)');
|
||||
userAdd.run(name, pass);
|
||||
}
|
||||
|
||||
function passwordQuery(user) {
|
||||
let passwordQuery = database.prepare('SELECT passwordHash FROM credentials WHERE username = ?;');
|
||||
return passwordQuery.get(user)
|
||||
}
|
||||
|
||||
const authDB = {
|
||||
prepareDB,
|
||||
get userCheck() { return userCheck; },
|
||||
get userAdd() { return userAdd; },
|
||||
get passwordQuery() { return passwordQuery; },
|
||||
checkUser,
|
||||
addUser,
|
||||
passwordQuery,
|
||||
RESERVED_USERNAMES
|
||||
};
|
||||
|
||||
|
@ -8,16 +8,3 @@ import authDB from './authDB.js';
|
||||
export function isValidString(value) {
|
||||
return typeof value === 'string' && value.trim() !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {import('better-sqlite3').Statement} userCheck
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function checkUser(name, userCheck) {
|
||||
const result = authDB.userCheck.get(name);
|
||||
const key = Object.keys(result)[0];
|
||||
|
||||
return result[key] === 1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user