From da74f5e3e1197f9195362a6e951bd2f69320c871 Mon Sep 17 00:00:00 2001 From: Rafi Arrafif Date: Wed, 11 Mar 2026 09:24:25 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=A6=20chore:=20snapshot=20commit=20bef?= =?UTF-8?q?ore=20major=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../getUserHeaderInformation/index.ts | 18 ++---- .../controllers/googleCallback.controller.ts | 2 +- .../controllers/googleRequest.controller.ts | 12 ++-- src/modules/auth/index.ts | 9 ++- .../schemas/getCallbackProviderUrl.schema.ts | 45 ++++++++++++++ .../auth/schemas/googleCallback.schema.ts | 58 +++++++++++++++++++ .../auth/schemas/googleRequest.schema.ts | 54 +++++++++++++++++ 7 files changed, 175 insertions(+), 23 deletions(-) create mode 100644 src/modules/auth/schemas/getCallbackProviderUrl.schema.ts create mode 100644 src/modules/auth/schemas/googleCallback.schema.ts create mode 100644 src/modules/auth/schemas/googleRequest.schema.ts diff --git a/src/helpers/http/userHeader/getUserHeaderInformation/index.ts b/src/helpers/http/userHeader/getUserHeaderInformation/index.ts index c704a50..a249340 100644 --- a/src/helpers/http/userHeader/getUserHeaderInformation/index.ts +++ b/src/helpers/http/userHeader/getUserHeaderInformation/index.ts @@ -10,25 +10,15 @@ export interface ClientInfoHeader { ip: string; } -export const getUserHeaderInformation = ( - ctx: Context, -): UserHeaderInformation => { +export const getUserHeaderInformation = (ctx: Context): UserHeaderInformation => { const clientInfoHeader = - (JSON.parse( - ctx.request.headers.get("x-client-info") as string, - ) as ClientInfoHeader) ?? ("unknown" as string); + (JSON.parse(ctx.request.headers.get("x-client-info") as string) as ClientInfoHeader) ?? ("unknown" as string); const userHeaderInformation = { ip: clientInfoHeader.ip ?? "unknown", deviceType: clientInfoHeader.deviceType ?? "unknown", - deviceOS: - (clientInfoHeader.os ?? "unknown") + - " " + - (clientInfoHeader.osVersion ?? "unknown"), - browser: - (clientInfoHeader.browser ?? "unknown") + - " " + - (clientInfoHeader.browserVersion ?? "unknown"), + deviceOS: (clientInfoHeader.os ?? "unknown") + " " + (clientInfoHeader.osVersion ?? "unknown"), + browser: (clientInfoHeader.browser ?? "unknown") + " " + (clientInfoHeader.browserVersion ?? "unknown"), }; return userHeaderInformation; diff --git a/src/modules/auth/controllers/googleCallback.controller.ts b/src/modules/auth/controllers/googleCallback.controller.ts index fcb501e..b02cf59 100644 --- a/src/modules/auth/controllers/googleCallback.controller.ts +++ b/src/modules/auth/controllers/googleCallback.controller.ts @@ -5,7 +5,7 @@ import { googleCallbackService } from "../services/http/googleCallback.service"; import { getUserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation"; export const googleCallbackController = async ( - ctx: Context & { query: { code: string; state: string; callbackURI: string } } + ctx: Context & { query: { code: string; state: string; callbackURI: string } }, ) => { try { const userHeaderInfo = getUserHeaderInformation(ctx); diff --git a/src/modules/auth/controllers/googleRequest.controller.ts b/src/modules/auth/controllers/googleRequest.controller.ts index 7b122a2..a778d8b 100644 --- a/src/modules/auth/controllers/googleRequest.controller.ts +++ b/src/modules/auth/controllers/googleRequest.controller.ts @@ -1,14 +1,16 @@ -import { Context } from "elysia"; +import { Context, Static } from "elysia"; import { mainErrorHandler } from "../../../helpers/error/handler"; import { googleRequestService } from "../services/http/googleRequest.service"; import { returnReadResponse } from "../../../helpers/callback/httpResponse"; +import { googleRequestSchema } from "../schemas/googleRequest.schema"; -export const googleRequestController = async ( - ctx: Context & { query: { callback?: string } } -) => { +export const googleRequestController = async (ctx: { + set: Context["set"]; + query: Static; +}) => { try { const loginUrl = await googleRequestService(ctx.query.callback); - return returnReadResponse(ctx.set, 200, "Google login url created!", { + return returnReadResponse(ctx.set, 200, "Google login URL created successfully.", { endpointUrl: loginUrl, }); } catch (error) { diff --git a/src/modules/auth/index.ts b/src/modules/auth/index.ts index 55b4263..72e8076 100644 --- a/src/modules/auth/index.ts +++ b/src/modules/auth/index.ts @@ -9,13 +9,16 @@ import { tokenValidationController } from "./controllers/tokenValidation.control import { logoutController } from "./controllers/logout.controller"; import { tokenValidationSchema } from "./schemas/tokenValidation.schema"; import { getOauthProvidersSchema } from "./schemas/getOauthProviders.schema"; +import { getCallbackProviderUrlSchema } from "./schemas/getCallbackProviderUrl.schema"; +import { googleRequestSchema } from "./schemas/googleRequest.schema"; +import { googleCallbackSchema } from "./schemas/googleCallback.schema"; export const authModule = new Elysia({ prefix: "/auth", tags: ["Authentication"] }) .post("/token/validate", tokenValidationController, tokenValidationSchema) .get("/providers", getOauthProvidersController, getOauthProvidersSchema) - .get("/providers/:name/callback", getCallbackProviderUrlController) + .get("/providers/:name/callback", getCallbackProviderUrlController, getCallbackProviderUrlSchema) + .get("/google", googleRequestController, googleRequestSchema) + .get("/google/callback", googleCallbackController, googleCallbackSchema) .get("/github", githubRequestController) .get("/github/callback", githubCallbackController) - .get("/google", googleRequestController) - .get("/google/callback", googleCallbackController) .post("/logout", logoutController); diff --git a/src/modules/auth/schemas/getCallbackProviderUrl.schema.ts b/src/modules/auth/schemas/getCallbackProviderUrl.schema.ts new file mode 100644 index 0000000..cef86cb --- /dev/null +++ b/src/modules/auth/schemas/getCallbackProviderUrl.schema.ts @@ -0,0 +1,45 @@ +import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema"; + +export const getCallbackProviderUrlSchema = { + detail: { + summary: "Get the callback URL of oauth provider", + description: + "After users have successfully completed the authentication process on the OAuth provider page, they will be redirected to the callback page on the frontend. This endpoint aims to obtain the actual endpoint for each OAuth response handler.", + responses: { + 200: { + description: "The callback URL on the provider has been found.", + content: { + "application/json": { + schema: { + type: "object", + properties: { + success: { + type: "boolean", + default: true, + }, + status: { + type: "number", + default: 200, + }, + message: { + type: "string", + default: "The callback URL on the provider has been found.", + }, + data: { + type: "object", + properties: { + callback_url: { + type: "string", + description: "The callback URL on the provider.", + example: "auth/google/callback", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} satisfies AppRouteSchema; diff --git a/src/modules/auth/schemas/googleCallback.schema.ts b/src/modules/auth/schemas/googleCallback.schema.ts new file mode 100644 index 0000000..c5249fa --- /dev/null +++ b/src/modules/auth/schemas/googleCallback.schema.ts @@ -0,0 +1,58 @@ +import { t } from "elysia"; +import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema"; + +export const googleCallbackSchema = { + headers: t.Object({ + "x-client-info": t.String({ + examples: [ + '{"os":"Windows","osVersion":"10","browser":"Chrome","browserVersion":"89.0.4389.82","deviceType":"Desktop","ip":"192.168.1.1"}', + ], + }), + }), + query: t.Object({ + code: t.String({ examples: ["4/0AY0e-xxxxxxxxx"] }), + state: t.String({ examples: ["random_state_string"] }), + callbackURI: t.String({ examples: ["https://example.com/auth/google/callback"] }), + }), + detail: { + summary: "Google OAuth callback endpoint", + description: + "Handles the callback from Google OAuth and processes the authentication response. This endpoint also processes the account provisioning if the user is logging in for the first time.", + responses: { + 200: { + description: "Authentication successful", + content: { + "application/json": { + schema: { + type: "object", + properties: { + success: { + type: "boolean", + example: true, + }, + status: { + type: "number", + example: 200, + }, + message: { + type: "string", + example: "Authentication successful", + }, + data: { + type: "object", + properties: { + authToken: { + type: "string", + description: "JWT token for authenticated user", + example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} satisfies AppRouteSchema; diff --git a/src/modules/auth/schemas/googleRequest.schema.ts b/src/modules/auth/schemas/googleRequest.schema.ts new file mode 100644 index 0000000..78b4e8c --- /dev/null +++ b/src/modules/auth/schemas/googleRequest.schema.ts @@ -0,0 +1,54 @@ +import { t } from "elysia"; +import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema"; + +export const googleRequestSchema = { + query: t.Object({ + callback: t.Optional( + t.String({ + description: "The callback URL to redirect after Google authentication. It should be URL-encoded if provided.", + }), + ), + }), + detail: { + summary: "Initiate Google OAuth flow", + description: + "This endpoint initiates the Google OAuth flow by redirecting the user to Google's authentication page.", + responses: { + 200: { + description: "Google login URL created successfully.", + content: { + "application/json": { + schema: { + type: "object", + properties: { + success: { + type: "boolean", + default: true, + }, + status: { + type: "number", + default: 200, + }, + message: { + type: "string", + default: "Google login URL created successfully.", + }, + data: { + type: "object", + properties: { + endpointUrl: { + type: "string", + description: "The URL to redirect the user for Google authentication.", + example: + "https://accounts.google.com/o/oauth2/v2/auth?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=email%20profile", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} satisfies AppRouteSchema;