From fe7f2f657798fe56c67ff8ed437054859623dcd6 Mon Sep 17 00:00:00 2001 From: flowcore-platform Date: Mon, 9 Dec 2024 10:28:46 +0000 Subject: [PATCH] feat: added token decorator --- .github/workflows/test.yml | 6 ++---- src/library/decorator/index.ts | 5 +++-- src/library/decorator/token.decorator.ts | 21 +++++++++++++++++++++ src/library/guard/auth.guard.ts | 11 ++++++----- test/fixtures/test.resolver.ts | 7 +++++++ test/oidc-protect.module.spec.ts | 20 ++++++++++++++++++++ test/test.schema.gql | 1 + 7 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 src/library/decorator/token.decorator.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 464c417..b3bafd1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,15 +2,13 @@ name: Test on: push: - branches-ignore: [ "main" ] + branches-ignore: ["main"] env: NODE_VERSION: ">=18.12.1" jobs: - test: - runs-on: ubuntu-latest steps: @@ -30,7 +28,7 @@ jobs: run: yarn lint - name: Start Keycloak run: | - docker-compose -f test/docker/docker-compose.yaml up -d + docker compose -f test/docker/docker-compose.yaml up -d chmod +x test/docker/await-testing.sh test/docker/await-testing.sh - name: Test diff --git a/src/library/decorator/index.ts b/src/library/decorator/index.ts index f7f2e5a..05000de 100644 --- a/src/library/decorator/index.ts +++ b/src/library/decorator/index.ts @@ -1,4 +1,5 @@ +export * from "./authenticated-user.decorator"; export * from "./public.decorator"; -export * from "./resource-roles.decorator"; export * from "./realm-roles.decorator"; -export * from "./authenticated-user.decorator"; +export * from "./resource-roles.decorator"; +export * from "./token.decorator"; diff --git a/src/library/decorator/token.decorator.ts b/src/library/decorator/token.decorator.ts new file mode 100644 index 0000000..2837dcd --- /dev/null +++ b/src/library/decorator/token.decorator.ts @@ -0,0 +1,21 @@ +import { createParamDecorator, type ExecutionContext } from "@nestjs/common"; +import { OidcProtectService } from "../oidc-protect/oidc-protect.service"; + +export interface TokenOptions { + required?: boolean; + storedIn?: string; +} + +export const Token = createParamDecorator( + async (data, ctx: ExecutionContext) => { + const request = await OidcProtectService.getRequest(ctx); + + const { required = true, storedIn = "token" } = data || {}; + + if (!request[storedIn] && required) { + throw new Error("Token not found"); + } + + return request[storedIn]; + }, +); diff --git a/src/library/guard/auth.guard.ts b/src/library/guard/auth.guard.ts index 5a8d57a..4cd17dc 100644 --- a/src/library/guard/auth.guard.ts +++ b/src/library/guard/auth.guard.ts @@ -1,14 +1,14 @@ +import { InjectLogger, LoggerService } from "@flowcore/microservice"; import { CanActivate, ExecutionContext, Injectable, - UnauthorizedException, + UnauthorizedException } from "@nestjs/common"; -import { InjectLogger, LoggerService } from "@flowcore/microservice"; -import dayjs from "dayjs"; -import { OidcProtectService } from "../oidc-protect/oidc-protect.service"; import { Reflector } from "@nestjs/core"; +import dayjs from "dayjs"; import { PUBLIC_OPERATION_KEY } from "../decorator/public.decorator"; +import { OidcProtectService } from "../oidc-protect/oidc-protect.service"; @Injectable() export class AuthGuard implements CanActivate { @@ -56,7 +56,7 @@ export class AuthGuard implements CanActivate { } request.authenticatedUser = decodedToken; - + request.token = token; return true; } @@ -78,6 +78,7 @@ export class AuthGuard implements CanActivate { } request.authenticatedUser = decodedToken; + request.token = token; return true; } catch (e) { return true; diff --git a/test/fixtures/test.resolver.ts b/test/fixtures/test.resolver.ts index 242dc72..e13e0a7 100644 --- a/test/fixtures/test.resolver.ts +++ b/test/fixtures/test.resolver.ts @@ -4,6 +4,7 @@ import { Public, RealmRoles, ResourceRoles, + Token, } from "../../src"; import { CLIENT_ROLE, REALM_ROLE } from "./keycloak/keycloak-prep.service"; @@ -39,4 +40,10 @@ export class TestResolver { public authenticatedUser(@AuthenticatedUser() user: any): string { return user.preferred_username; } + + @RealmRoles([REALM_ROLE]) + @Query(() => String, { name: "token" }) + public token(@Token() token: string): string { + return token; + } } diff --git a/test/oidc-protect.module.spec.ts b/test/oidc-protect.module.spec.ts index a121cba..7a5f34d 100644 --- a/test/oidc-protect.module.spec.ts +++ b/test/oidc-protect.module.spec.ts @@ -286,6 +286,26 @@ describe("OIDC Protect Module", () => { ); }); + it("should get access to token", async () => { + const token = await ( + await app.resolve(KeycloakPrepService) + ).getUserToken(UserType.TEST_USER); + + const decoded: any = jwtDecode(token); + + const response = await queryGraphQLEndpoint( + app, + gql` + query { + token + } + `, + token, + ); + + expect(response.body.data.token).toBe(token); + }); + it("should return forbidden with no access to resource role", async () => { const token = await ( await app.resolve(KeycloakPrepService) diff --git a/test/test.schema.gql b/test/test.schema.gql index 9fa5a93..6ee5051 100644 --- a/test/test.schema.gql +++ b/test/test.schema.gql @@ -15,5 +15,6 @@ type Query { realmRole: String! resourceRole: String! authenticatedUser: String! + token: String! testPerson: TestPerson! } \ No newline at end of file