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

feat: add refresh token - fix #22 #35

Merged
merged 1 commit into from
Jul 27, 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
1 change: 1 addition & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"tasks": {
"start": "deno run -A --watch=./src --unstable-kv mod.ts",
"start:nowatch": "deno run -A --unstable-kv mod.ts",

"compile": "deno compile -A --unstable-kv --no-check",

Expand Down
17 changes: 15 additions & 2 deletions src/modules/api/v1/account/account.http
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Content-Type: application/json

{
"username": "pagoru123",
"password": "123456"
"password": "123456",
"captchaId": ""
}

###
Expand All @@ -15,7 +16,19 @@ Content-Type: application/json

{
"username": "pagoru123",
"password": "123456"
"password": "123456",
"captchaId": ""
}

###

# Login
POST http://localhost:2024/v1/account/refresh-session
Content-Type: application/json

{
"sessionId": "iZyHMLpYMUFVutkH",
"refreshToken": "bMjnGYbydaMUUM89Qbovx91fCR1H1LXAYvHzPbPrPf5GiuGePWbBhMd9dTSiGIdNY7nfeqhV6nTwEu22UTO4IuZJ680faXRgumnxKnIYvmJORFvkAj4ctRTKafuNe1QW"
}

###
42 changes: 35 additions & 7 deletions src/modules/api/v1/account/login.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { RequestMethod } from "shared/enums/main.ts";
import { System } from "system/main.ts";
import * as bcrypt from "bcrypt";
import { getRandomString } from "shared/utils/main.ts";
import {
REFRESH_TOKEN_EXPIRE_TIME,
SESSION_EXPIRE_TIME,
} from "shared/consts/main.ts";

export const loginRequest: RequestType = {
method: RequestMethod.POST,
Expand All @@ -20,7 +24,6 @@ export const loginRequest: RequestType = {

const { value: account } = await System.db.get(["accounts", username]);

console.log(account);
if (!account)
return Response.json(
{ status: 403 },
Expand All @@ -40,15 +43,39 @@ export const loginRequest: RequestType = {
);

const sessionId = getRandomString(16);

const token = getRandomString(64);
const hash = bcrypt.hashSync(token, bcrypt.genSaltSync(8));

const refreshToken = getRandomString(128);
const refreshHash = bcrypt.hashSync(refreshToken, bcrypt.genSaltSync(8));

if (account.sessionId) {
await System.db.delete(["session", account.sessionId]);
await System.db.delete(["refresh-session", account.sessionId]);
}

const salt = bcrypt.genSaltSync(8);
const hash = bcrypt.hashSync(token, salt);
await System.db.set(
["session", sessionId],
{
hash,
accountId: account.accountId,
},
{ expireIn: SESSION_EXPIRE_TIME },
);
await System.db.set(
["refresh-session", sessionId],
{
hash: refreshHash,
accountId: account.accountId,
username,
},
{ expireIn: REFRESH_TOKEN_EXPIRE_TIME },
);

await System.db.set(["session", sessionId], {
hash,
accountId: account.accountId,
expireIn: 1000 * 60 * 5,
await System.db.set(["accounts", username], {
...account,
sessionId,
});

return Response.json(
Expand All @@ -57,6 +84,7 @@ export const loginRequest: RequestType = {
data: {
sessionId,
token,
refreshToken,
username,
},
},
Expand Down
3 changes: 2 additions & 1 deletion src/modules/api/v1/account/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { getPathRequestList } from "shared/utils/main.ts";

import { loginRequest } from "./login.request.ts";
import { registerRequest } from "./register.request.ts";
import { refreshSessionRequest } from "./refresh-session.request.ts";

export const accountRequestList: RequestType[] = getPathRequestList({
requestList: [loginRequest, registerRequest],
requestList: [loginRequest, registerRequest, refreshSessionRequest],
pathname: "/account",
});
106 changes: 106 additions & 0 deletions src/modules/api/v1/account/refresh-session.request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { RequestType } from "shared/types/main.ts";
import { RequestMethod } from "shared/enums/main.ts";
import { System } from "system/main.ts";
import * as bcrypt from "bcrypt";
import { getRandomString } from "shared/utils/main.ts";
import {
REFRESH_TOKEN_EXPIRE_TIME,
SESSION_EXPIRE_TIME,
} from "shared/consts/main.ts";

export const refreshSessionRequest: RequestType = {
method: RequestMethod.POST,
pathname: "/refresh-session",
func: async (request, url) => {
let { sessionId, refreshToken } = await request.json();

if (!sessionId || !refreshToken)
return Response.json(
{ status: 403 },
{
status: 403,
},
);

const { value: refreshSession } = await System.db.get([
"refresh-session",
sessionId,
]);

if (!refreshSession)
return Response.json(
{ status: 403 },
{
status: 403,
},
);

const result = bcrypt.compareSync(refreshToken, refreshSession.hash);

if (!result)
return Response.json(
{ status: 403 },
{
status: 403,
},
);

await System.db.delete(["session", sessionId]);
await System.db.delete(["refresh-session", sessionId]);

const { value: account } = await System.db.get([
"accounts",
refreshSession.username,
]);

if (!account)
return Response.json(
{ status: 403 },
{
status: 403,
},
);

const token = getRandomString(64);
const hash = bcrypt.hashSync(token, bcrypt.genSaltSync(8));

refreshToken = getRandomString(128);
const refreshHash = bcrypt.hashSync(refreshToken, bcrypt.genSaltSync(8));

await System.db.set(
["session", sessionId],
{
hash,
accountId: account.accountId,
},
{ expireIn: SESSION_EXPIRE_TIME },
);
await System.db.set(
["refresh-session", sessionId],
{
hash: refreshHash,
accountId: account.accountId,
username: account.username,
},
{ expireIn: REFRESH_TOKEN_EXPIRE_TIME },
);

await System.db.set(["accounts", account.username], {
...account,
sessionId,
});

return Response.json(
{
status: 200,
data: {
sessionId,
token,
refreshToken,
username: account.username,
},
},
{ status: 200 },
);
},
};
4 changes: 2 additions & 2 deletions src/modules/api/v1/hotel/hotel.http
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Content-Type: application/json

{
"username": "pagoru123",
"sessionId": "m58T8hGSKUBnv016",
"token": "8k0Afr58uYQi7Lv40XtcR5Bi7S99CV1BKPxrzw0nGbSDODlLUFYYFKrnjtuxnE5O"
"sessionId": "iZyHMLpYMUFVutkH",
"token": "CnhwAZ7ZbNGfOWlhBoyNRsPD5SmDVMY8BVO2rSzFbpm9JblhsPvh73xZNUIvc2k8"
}

###
8 changes: 8 additions & 0 deletions src/modules/api/v1/hotel/verify-session.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ export const verifySessionRequest: RequestType = {

// Delete current session
await System.db.delete(["session", sessionId]);
// Check if session is current
if (account.sessionId !== sessionId)
return Response.json(
{
status: 403,
},
{ status: 403 },
);

return Response.json(
{
Expand Down
1 change: 1 addition & 0 deletions src/shared/consts/main.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./config.consts.ts";
export * from "./session.consts.ts";
4 changes: 4 additions & 0 deletions src/shared/consts/session.consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 5 mins
export const SESSION_EXPIRE_TIME = 1000 * 60 * 5;
// 7 days
export const REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7;
Loading