Skip to content

Commit

Permalink
Co-authored-by: Garv <[email protected]>, working user …
Browse files Browse the repository at this point in the history
…authentication
  • Loading branch information
DeveloperMindset123 committed Sep 28, 2024
1 parent bc05018 commit 2f8fc16
Show file tree
Hide file tree
Showing 16 changed files with 994 additions and 96 deletions.
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"main": "expo-router/entry",
"scripts": {
"test-server": "npx ts-node-dev --respawn src/lib/api/auth/test.routes.ts",
"server": "npx tsx src/lib/api/server.ts",
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
Expand Down Expand Up @@ -28,11 +30,14 @@
"@rneui/themed": "^4.0.0-rc.8",
"@shopify/flash-list": "1.6.4",
"@types/bcrypt": "^5.0.2",
"@types/express": "^5.0.0",
"@types/jsonwebtoken": "^9.0.7",
"@types/uuid": "^10.0.0",
"bcrypt": "^5.1.1",
"bcrypt-ts": "^5.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"crypto-ts": "^1.0.2",
"expo": "51.0.34",
"expo-device": "~6.0.2",
"expo-font": "~12.0.10",
Expand All @@ -43,6 +48,7 @@
"expo-status-bar": "~1.12.1",
"expo-updates": "0.25.25",
"expo-web-browser": "~13.0.3",
"express": "^4.21.0",
"jsonwebtoken": "^9.0.2",
"lucide-react-native": "^0.359.0",
"nativewind": "^4.0.22",
Expand Down Expand Up @@ -74,6 +80,8 @@
"prisma": "^5.20.0",
"react-native-dotenv": "^3.4.11",
"tailwindcss": "3.3.2",
"ts-node-dev": "^2.0.0",
"tsx": "^4.19.1",
"typescript": "5.3.3"
},
"resolutions": {
Expand Down
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ generator client {

datasource db {
provider = "postgresql"
// this is where the databse connection tkaes place
url = env("DATABASE_URL")
}

Expand Down
68 changes: 68 additions & 0 deletions src/lib/api/auth/auth.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// TODO : remove this later once the values are being used
/* eslint-disable @typescript-eslint/no-unused-vars */
import express, { Router } from 'express';
import { v4 as uuidv4 } from 'uuid';
import { generateAccessAndRefreshTokens } from '../../utils/jwt';
import { addRefreshTokenToWhiteList } from './auth.services';
import { findUserByEmail, createUserByEmailAndPassword } from '../users/users.services';

const app = express();
//@see https://expressjs.com/en/guide/routing.html
const authRouter = Router();

// TODO : Test this route using CURL
app.post('/register', async (req, res, next) => {
try {
// object destructuring to help retrieve
// @see https://www.geeksforgeeks.org/how-to-post-json-data-using-curl/
// this is the data we provide during an API call
const { email, password } = req.body;
if (!email || !password) {
res.status(400);
throw new Error('You must provide an email and a password.');
}

const existingUser = await findUserByEmail(email);

if (existingUser) {
res.status(404).send({
message: `The email ${email} is already in use, please login instead.`,
});
throw new Error('The email is already in use');
}

const user = await createUserByEmailAndPassword({ email, password });
// @see https://stackoverflow.com/questions/20342058/which-uuid-version-to-use
const jti = uuidv4();
const { accessToken, refreshToken } = generateAccessAndRefreshTokens(user, jti);
// TODO : remove this comment maybe
// jti is the unique id assigned to the newly created user, think of it as the primary key
await addRefreshTokenToWhiteList({ jti, refreshToken, userId: user.id });
/*res.status(200).send({
message: 'Successfully created new user!',
}); */

res
.json({
accessToken,
refreshToken,
})
.status(200);
} catch (err) {
//next(err);
console.error(err);
}
});

//export default authRouter;
/*
app.use('/auth', authRouter);
app.listen('4000', () => {
try {
console.log('Connection successful!');
} catch (err) {
console.error(err);
}
});
*/
export { authRouter };
49 changes: 33 additions & 16 deletions src/lib/api/auth/auth.services.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,61 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import db from '@/lib/utils/db';
import hashToken from '@/lib/utils/hashToken';

// TODO : too many errors being raised with using any
import { db } from '../../utils/db';
import * as cryptoTS from 'crypto-ts';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
//import { encryptToken } from '@/lib/utils/tokenization';
// TODO : Remove later

function encryptToken(token: any) {
const cipherText = cryptoTS.AES.encrypt(
JSON.stringify(token),
process.env.MY_ENCRYPTION_KEY ? process.env.MY_ENCRYPTION_KEY.toString() : 'MYENCRYPTIONKEY'
);
return cipherText.toString();
}

function decryptToken(token: any) {
const cipherText = encryptToken(token);
const bytes = cryptoTS.AES.decrypt(
cipherText.toString(),
process.env.MY_DECRYPTION_KEY ? process.env.MY_DECRYPTION_KEY.toString() : 'MYDECRYPTIONKEY'
);
const decryptedData = JSON.parse(bytes.toString(cryptoTS.enc.Utf8));
return decryptedData;
}
//import crypto from 'crypto';
//import { PrismaClient } from "@prisma/client";

/*
const db = new PrismaClient();
export default function hashToken(token: crypto.BinaryLike) {
// TODO : hashing logic, modify as needed
return crypto.createHash('sha512').update(token).digest('hex');
} */
export function addRefreshTokenToWhiteList({
jti,
refreshToken,
userId,
}: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
jti: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
refreshToken: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
userId: any;
}) {
return db.refreshTokens.create({
data: {
id: jti,
hashedToken: hashToken(refreshToken),
hashedToken: encryptToken(refreshToken),
userId,
},
});
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function findRefreshTokenById(id: any) {
return db.refreshTokens.findUnique({
where: {
id,
},
});
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function deleteRefreshToken(id: any) {
return db.refreshTokens.updateMany({
where: {
Expand All @@ -45,8 +66,6 @@ export function deleteRefreshToken(id: any) {
},
});
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function revokeTokens(userId: any) {
return db.refreshTokens.updateMany({
where: {
Expand All @@ -57,5 +76,3 @@ export function revokeTokens(userId: any) {
},
});
}

// TODO : Continue at STEP 7
9 changes: 9 additions & 0 deletions src/lib/api/auth/test.rest
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

POST http://localhost:4001/register
content-type: application/json

{
"email" :"[email protected]",
"password":"123123asdasd"
}

60 changes: 60 additions & 0 deletions src/lib/api/auth/test.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// ? for learning/refresher on how routes work, not relevant to project
// ! Use this for dirty playground for testing/defining any preliminary api calls
//import express from 'express';

// TODO : file for running and testing to better understand how express works as a whole
/*
const app = express();
// define some example routes
// hello world for express
// this
// @param1 method or path for API call being made
// @param2 handler function to determine what to do once the request is recieved and what kind of response logic should take place
app.get('/testRoute', function (_req, res) {
res.send('Successful api call');
// the following are various mthods
// one mthod of using it is to retireve a file from the request and download it to local storage
res.download('../auth/test.rest');
res.jsonp({
// it can be any message as needed
message: 'File has been donwloaded',
});
// end the response
res.end();
});
app.get('/retrieveID', async (req, res) => {
//@see https://stackoverflow.com/questions/17007997/how-to-access-the-get-parameters-after-in-express
// explains how to retrieve query parameters made during an API call
const queryParams = await req.query.id;
res.send(`The query param is : ${queryParams}`);
res.end();
});
// define where the app should be running, meaning define the custom port
//@example call
/**
* curl -X GET "http://localhost:4000/retrieveID?id=
10"
* must be encased around string based values
// ! understanding how the next() function, aka callback function, also known as middleware works in this scenario
// next allow us to control api flow, and also allows us to overload and call on the same route more than once
app.get('/testRouteWithCallback', function (_req, res, next) {
res.write('This should execute\n');
next();
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
app.get('/testRouteWithCallback', function (_req, res, next) {
res.write('This should execute after');
// terminates the request
next();
res.end();
});
app.listen(4000, () => {
console.log('App listening to port 5000!');
});
*/
80 changes: 80 additions & 0 deletions src/lib/api/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
//! use this to add all the relevant main routes here

//import { authRouter } from './auth/auth.routes';
import express from 'express';

const app = express();
// ** needed to add express.json()
app.use(express.json());
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const router = express.Router();
//router.use('/auth', authRouter);
//http://localhost:4001/auth/register

app.get('/test', (req, res) => {
console.log('hello world');
res.send('Hello from A!').status(200);
});

app.listen('4001', () => {
console.log(`Listening to port 4001`);
});

import { v4 as uuidv4 } from 'uuid';
import { generateAccessAndRefreshTokens } from '../utils/jwt';
import { addRefreshTokenToWhiteList } from './auth/auth.services';
import { findUserByEmail, createUserByEmailAndPassword } from './users/users.services';

//const app = express();
//@see https://expressjs.com/en/guide/routing.html
const authRouter = express.Router();

// TODO : Test this route using CURL
// eslint-disable-next-line @typescript-eslint/no-unused-vars
app.post('/register', async (req, res, next) => {
try {
// @see https://www.geeksforgeeks.org/how-to-post-json-data-using-curl/
// ! issue here
if (req.body) {
console.log('body', req.body);
// console.log('lot', JSON.parse(req.body));
} else {
console.log('Unable to extract data');
}

//return;
const { email, password } = req.body;
if (!email || !password) {
res.status(400);
throw new Error('You must provide an email and a password.');
}

const existingUser = await findUserByEmail(email);

if (existingUser) {
res.status(404).send({
message: `The email ${email} is already in use, please login instead.`,
});
throw new Error('The email is already in use');
}

const user = await createUserByEmailAndPassword({ email, password });
// @see https://stackoverflow.com/questions/20342058/which-uuid-version-to-use
const jti = uuidv4();
const { accessToken, refreshToken } = generateAccessAndRefreshTokens(user, jti);
// TODO : remove this comment maybe
// jti is the unique id assigned to the newly created user, think of it as the primary key
await addRefreshTokenToWhiteList({ jti, refreshToken, userId: user.id });

res
.json({
accessToken,
refreshToken,
})
.status(200);
} catch (err) {
//next(err);
console.error(err);
}
});
5 changes: 2 additions & 3 deletions src/lib/api/users/users.services.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { hashSync } from 'bcrypt-ts';
import db from '@/lib/utils/db';
import { db } from "/Users/ayandas/Desktop/VS_Code_Projects/CCNY_SchedulePro/src/lib/utils/db";

// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
function findUserByEmail(email: any) {
export function findUserByEmail(email: any) {
return db.user.findUnique({
where: {
email,
Expand All @@ -12,7 +12,6 @@ function findUserByEmail(email: any) {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createUserByEmailAndPassword(user: any) {
// TODO : Modify this as needed
user.password = hashSync(user.password, 12);
return db.user.create({
data: user,
Expand Down
2 changes: 2 additions & 0 deletions src/lib/env-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const schema = z.object({
SEGMENT_KEY: z.string().url().optional(),
JWT_ACCESS_SECRET: z.string().url().optional(),
JWT_REFRESH_SECRET: z.string().url().optional(),
MY_ENCRYPTION_KEY: z.string().url().optional(),
MY_DECRYPTION_KEY: z.string().url().optional(),
});

const parsed = schema.safeParse(process.env);
Expand Down
3 changes: 1 addition & 2 deletions src/lib/utils/db.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
import { PrismaClient } from '@prisma/client';
const db = new PrismaClient();
export default db;
export const db = new PrismaClient();
6 changes: 0 additions & 6 deletions src/lib/utils/hashToken.ts

This file was deleted.

Loading

0 comments on commit 2f8fc16

Please sign in to comment.