Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added guest login and register flow #26

Merged
merged 1 commit into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions controllers/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,37 @@ export default {
}
},

registerGuest: async (request: Request, response: Response) => {
try {
const { username, password } = request.body;
const userId = (request as AuthenticatedRequest).tokenData.userId;

const existingUser = await userService.getUserById(userId);

if (!existingUser || existingUser.role !== 'guest') {
return response.status(401).json({ error: 'Only guests can register through this endpoint' });
}

const userWithUsername = await userService.getUserByUsername(username);

if (userWithUsername !== null) {
return response.status(400).json({ error: 'Username already taken' });
}

const updatedUser = await authService.registerGuest(userId, username, password);
const token = await authService.createTokenSession(updatedUser, password);

if (!token) {
throw new Error('Error creating token session');
}

return response.status(200).json({ token });
} catch (error) {
console.error('Error during guest registration:', error);
return response.status(500).send('An unexpected error occurred while registering as a guest');
}
},

login: async (request: Request, response: Response) => {
try {
const { username, password } = request.body;
Expand All @@ -51,6 +82,22 @@ export default {
}
},

guestLogin: async (request: Request, response: Response) => {
try {
const newUser = await authService.loginGuest();
const token = await authService.createGuestTokenSession(newUser);

if (!token) {
throw new Error('Error creating token session');
}

response.status(201).json({ token });
} catch (error) {
console.error('Error during guest login:', error);
return response.status(500).send('An unexpected error occurred while logging in as guest');
}
},

token: async (request: Request, response: Response) => {
try {
const { userId } = (request as AuthenticatedRequest).tokenData;
Expand Down
4 changes: 3 additions & 1 deletion database/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ CREATE TABLE quests (
INSERT INTO roles (name) VALUES
('user'),
('moderator'),
('admin');
('admin'),
('guest');


-- Insert categories into categories table
INSERT INTO categories (name) VALUES
Expand Down
2 changes: 1 addition & 1 deletion models/role.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
type Role = "admin" | "moderator" | "user";
type Role = "admin" | "moderator" | "user" | "guest";

export default Role;
11 changes: 11 additions & 0 deletions routes/auth.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,26 @@ authRouter.post(
validateBody(authSchema.register),
authController.register
);
authRouter.post(
'/register-guest',
validateBody(authSchema.register),
authenticate,
authController.registerGuest
);
authRouter.post(
'/login',
validateBody(authSchema.login),
authController.login
);
authRouter.post(
'/login-guest',
authController.guestLogin,
);
authRouter.post(
'/token',
authenticate,
authController.token
);


export default authRouter;
58 changes: 58 additions & 0 deletions services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Role from "../models/role";
import User, { metadata } from "../models/user";
import { comparePassword, hashPassword } from "../utils/passwordHash";
import { signToken } from "../utils/signToken";
import crypto from 'crypto';

export default {
register: async (username: string, password: string): Promise<User> => {
Expand All @@ -20,6 +21,58 @@ export default {
}
},

registerGuest: async (userId: number, username: string, password: string): Promise<User> => {
try {
const passwordHash = await hashPassword(password);

const queryString = `
UPDATE users
SET username = $1, password = $2, role_id = (SELECT id FROM roles WHERE name = 'user' LIMIT 1)
WHERE id = $3
RETURNING *
`;

const result = await executeQuery(queryString, [username, passwordHash, userId], true);

if (!result) {
throw new Error('Failed to update guest user to registered user');
}

return result;
} catch (error) {
console.error('Error during guest-to-user registration:', error);
throw new Error('Error during guest registration');
}
},

loginGuest: async (): Promise<User> => {
try {
const guestIdentifier = `anon-${crypto.randomBytes(12).toString('hex')}`;

const queryString = `
WITH inserted_user AS (
INSERT INTO users (username, role_id)
VALUES ($1, (SELECT id FROM roles WHERE name = 'guest' LIMIT 1))
RETURNING *
)
SELECT inserted_user.*, roles.name AS role
FROM inserted_user
JOIN roles ON inserted_user.role_id = roles.id
`;

const result = await executeQuery(queryString, [guestIdentifier], true);

if (!result || result.length === 0) {
throw new Error('Failed to insert guest user');
}

return result;
} catch (error) {
console.error('Error during guest login:', error);
throw new Error('Error during guest login');
}
},

createTokenSession: async (user: User, plaintextPassword: string): Promise<string | null> => {
const passwordsMatch = await comparePassword(plaintextPassword, user.password);

Expand All @@ -31,6 +84,11 @@ export default {
return signToken(tokenData);
},

createGuestTokenSession: async (user: User): Promise<string> => {
const tokenData = { userId: user.id, username: 'Anonymous', role: user.role, metadata: user.metadata };
return signToken(tokenData);
},

refreshToken: (userId: number, username: string, role: Role, metadata: metadata): string => {
const tokenData = { userId, username, role, metadata };

Expand Down