From b7447b3b47b69ee0fa03e2ee4105ecc0589dff1c Mon Sep 17 00:00:00 2001 From: Adrian Andersen Date: Tue, 28 Jan 2025 13:33:43 +0100 Subject: [PATCH] feat: move token endpoint into token controller --- backend/.adonisjs/api.ts | 20 +++++++ .../app/controllers/auth/tokens_controller.ts | 48 +++++++++++++++ .../token/refresh/refresh-token.validator.ts | 5 +- .../app/services/auth/token/token.endpoint.ts | 58 ------------------- backend/app/validators/token.ts | 7 +++ backend/start/routes.ts | 9 ++- backend/tests/refresh-token.validator.spec.ts | 2 +- 7 files changed, 84 insertions(+), 65 deletions(-) create mode 100644 backend/app/controllers/auth/tokens_controller.ts delete mode 100644 backend/app/services/auth/token/token.endpoint.ts create mode 100644 backend/app/validators/token.ts diff --git a/backend/.adonisjs/api.ts b/backend/.adonisjs/api.ts index c546a5d..3f6a3ef 100644 --- a/backend/.adonisjs/api.ts +++ b/backend/.adonisjs/api.ts @@ -7,6 +7,15 @@ import type { MakeTuyauRequest, MakeTuyauResponse } from "@tuyau/utils/types"; import type { InferInput } from "@vinejs/vine/types"; +type TokenPost = { + request: MakeTuyauRequest< + InferInput<(typeof import("../app/validators/token.ts"))["tokenValidator"]> + >; + response: MakeTuyauResponse< + import("../app/controllers/auth/tokens_controller.ts").default["token"], + true + >; +}; type AuthIdRedirectGetHead = { request: unknown; response: MakeTuyauResponse< @@ -22,6 +31,10 @@ type AuthIdCallbackGetHead = { >; }; export interface ApiDefinition { + token: { + $url: {}; + $post: TokenPost; + }; auth: { ":provider": { redirect: { @@ -38,6 +51,13 @@ export interface ApiDefinition { }; } const routes = [ + { + params: [], + name: "auth.token", + path: "/token", + method: ["POST"], + types: {} as TokenPost, + }, { params: ["provider"], name: "auth.social.redirect", diff --git a/backend/app/controllers/auth/tokens_controller.ts b/backend/app/controllers/auth/tokens_controller.ts new file mode 100644 index 0000000..c5d2e2d --- /dev/null +++ b/backend/app/controllers/auth/tokens_controller.ts @@ -0,0 +1,48 @@ +import { HttpContext } from "@adonisjs/core/http"; + +import RefreshTokenValidator from "#services/auth/token/refresh/refresh-token.validator"; +import TokenHandler from "#services/auth/token/token.handler"; +import BlResponseHandler from "#services/response/bl-response.handler"; +import { RefreshToken } from "#services/types/refresh-token"; +import { BlError } from "#shared/bl-error/bl-error"; +import { BlapiResponse } from "#shared/blapi-response/blapi-response"; +import { tokenValidator } from "#validators/token"; + +export default class TokensController { + async token(ctx: HttpContext) { + const { refreshToken } = await ctx.request.validateUsing(tokenValidator); + RefreshTokenValidator.validate(refreshToken).then( + // @ts-expect-error fixme: auto ignored + (validatedRefreshToken: RefreshToken) => { + TokenHandler.createTokens(validatedRefreshToken.username).then( + (jwTokens: { accessToken: string; refreshToken: string }) => { + BlResponseHandler.sendResponse( + ctx, + new BlapiResponse([ + { accessToken: jwTokens.accessToken }, + { refreshToken: jwTokens.refreshToken }, + ]), + ); + }, + (createTokenError: BlError) => { + BlResponseHandler.sendErrorResponse( + ctx, + new BlError("could not create tokens") + .store("oldRefreshToken", refreshToken) + .code(906) + .add(createTokenError), + ); + }, + ); + }, + (refreshTokenValidationError: BlError) => { + BlResponseHandler.sendErrorResponse( + ctx, + new BlError("refreshToken not valid") + .code(909) + .add(refreshTokenValidationError), + ); + }, + ); + } +} diff --git a/backend/app/services/auth/token/refresh/refresh-token.validator.ts b/backend/app/services/auth/token/refresh/refresh-token.validator.ts index 927d682..27382ab 100644 --- a/backend/app/services/auth/token/refresh/refresh-token.validator.ts +++ b/backend/app/services/auth/token/refresh/refresh-token.validator.ts @@ -3,11 +3,8 @@ import jwt from "jsonwebtoken"; import { BlError } from "#shared/bl-error/bl-error"; import env from "#start/env"; -function validate(refreshToken: string | undefined): Promise { +function validate(refreshToken: string) { return new Promise((resolve, reject) => { - if (!refreshToken || refreshToken.length <= 0) - return reject(new BlError("refreshToken is empty or undefined")); - try { jwt.verify( refreshToken, diff --git a/backend/app/services/auth/token/token.endpoint.ts b/backend/app/services/auth/token/token.endpoint.ts deleted file mode 100644 index ae0b46d..0000000 --- a/backend/app/services/auth/token/token.endpoint.ts +++ /dev/null @@ -1,58 +0,0 @@ -import router from "@adonisjs/core/services/router"; - -import RefreshTokenValidator from "#services/auth/token/refresh/refresh-token.validator"; -import TokenHandler from "#services/auth/token/token.handler"; -import BlResponseHandler from "#services/response/bl-response.handler"; -import { RefreshToken } from "#services/types/refresh-token"; -import { BlError } from "#shared/bl-error/bl-error"; -import { BlapiResponse } from "#shared/blapi-response/blapi-response"; - -function generateEndpoint() { - router.post("/token", (ctx) => { - const refreshToken = ctx.request.body()["refreshToken"]; - if (refreshToken) { - RefreshTokenValidator.validate(refreshToken).then( - // @ts-expect-error fixme: auto ignored - (validatedRefreshToken: RefreshToken) => { - TokenHandler.createTokens(validatedRefreshToken.username).then( - (jwTokens: { accessToken: string; refreshToken: string }) => { - BlResponseHandler.sendResponse( - ctx, - new BlapiResponse([ - { accessToken: jwTokens.accessToken }, - { refreshToken: jwTokens.refreshToken }, - ]), - ); - }, - (createTokenError: BlError) => { - BlResponseHandler.sendErrorResponse( - ctx, - new BlError("could not create tokens") - .store("oldRefreshToken", refreshToken) - .code(906) - .add(createTokenError), - ); - }, - ); - }, - (refreshTokenValidationError: BlError) => { - BlResponseHandler.sendErrorResponse( - ctx, - new BlError("refreshToken not valid") - .code(909) - .add(refreshTokenValidationError), - ); - }, - ); - } else { - BlResponseHandler.sendErrorResponse( - ctx, - new BlError("bad format").code(701), - ); - } - }); -} -const TokenEndpoint = { - generateEndpoint, -}; -export default TokenEndpoint; diff --git a/backend/app/validators/token.ts b/backend/app/validators/token.ts new file mode 100644 index 0000000..9028d41 --- /dev/null +++ b/backend/app/validators/token.ts @@ -0,0 +1,7 @@ +import vine from "@vinejs/vine"; + +export const tokenValidator = vine.compile( + vine.object({ + refreshToken: vine.string().jwt(), + }), +); diff --git a/backend/start/routes.ts b/backend/start/routes.ts index 37e9d03..c63cc91 100644 --- a/backend/start/routes.ts +++ b/backend/start/routes.ts @@ -1,11 +1,17 @@ import router from "@adonisjs/core/services/router"; import LocalAuth from "#services/auth/local/local.auth"; -import TokenEndpoint from "#services/auth/token/token.endpoint"; import CollectionEndpointCreator from "#services/collection-endpoint/collection-endpoint-creator"; const AuthSocialController = () => import("#controllers/auth/social_controller"); +const AuthTokensController = () => + import("#controllers/auth/tokens_controller"); + +/** + * auth token + */ +router.post("/token", [AuthTokensController, "token"]).as("auth.token"); /** * auth social @@ -18,6 +24,5 @@ router .as("auth.social.callback"); LocalAuth.generateEndpoints(); -TokenEndpoint.generateEndpoint(); CollectionEndpointCreator.generateEndpoints(); diff --git a/backend/tests/refresh-token.validator.spec.ts b/backend/tests/refresh-token.validator.spec.ts index eac9ef0..3aecca4 100644 --- a/backend/tests/refresh-token.validator.spec.ts +++ b/backend/tests/refresh-token.validator.spec.ts @@ -38,7 +38,7 @@ test.group("RefreshTokenValidator", async () => { "test", { expiresIn: "1s" }, (error, refreshToken) => { - RefreshTokenValidator.validate(refreshToken).catch( + RefreshTokenValidator.validate(refreshToken ?? "").catch( (rtokenError: BlError) => { rtokenError.getCode().should.be.eql(909); },