From 05b195926bfd53ce91219b9dde8fc0ec3fe24fd1 Mon Sep 17 00:00:00 2001 From: N08I40K Date: Sat, 25 Jan 2025 22:52:04 +0400 Subject: [PATCH 1/2] 3.0.1 - Switch VK ID authentication to use JWT tokens - Implement JWT verification and decoding - Validate JWT issuer and app ID - Add VKID OAuth integration and constants --- package.json | 3 ++- src/auth/auth.service.ts | 53 ++++++++++++++++++++++++++++--------- src/vkid/vkid.controller.ts | 2 -- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index c574f0c..5fc3966 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "schedule-parser-next", - "version": "3.0.0", + "version": "3.0.1", "description": "", "author": "N08I40K", "private": true, @@ -41,6 +41,7 @@ "dotenv": "^16.4.7", "firebase-admin": "^13.0.2", "jsdom": "^26.0.0", + "jsonwebtoken": "^9.0.2", "object-hash": "^3.0.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 3f788fa..76655c1 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -1,6 +1,7 @@ import { ConflictException, Injectable, + NotAcceptableException, UnauthorizedException, } from "@nestjs/common"; import { JwtService } from "@nestjs/jwt"; @@ -15,8 +16,8 @@ import SignUpErrorDto, { SignUpErrorCode } from "./dto/sign-up-error.dto"; import { SignInDto, SignInVKDto } from "./dto/sign-in.dto"; import ObjectID from "bson-objectid"; import UserDto from "../users/dto/user.dto"; -import { decodeJwt, verifyJwtSignature } from "firebase-admin/lib/utils/jwt"; import { vkIdConstants } from "../contants"; +import * as jwt from "jsonwebtoken"; @Injectable() export class AuthService { @@ -136,16 +137,6 @@ export class AuthService { * } */ private static async parseVKID(idToken: string): Promise { - try { - await verifyJwtSignature(idToken, vkIdConstants.jwtPubKey, { - issuer: "VK", - jwtid: "21", - }); - } catch { - return null; - } - - const decodedToken = await decodeJwt(idToken); type TokenData = { iis: string; sub: number; @@ -155,9 +146,45 @@ export class AuthService { jti: number; }; - const payload = decodedToken.payload as TokenData; + const payload = await new Promise((resolve, reject) => { + jwt.verify(idToken, vkIdConstants.jwtPubKey, (err, data) => { + if (err) return reject(new NotAcceptableException(err.message)); - if (payload.app !== vkIdConstants.clientId) return null; + const payload = data as unknown as TokenData; + + if (typeof payload !== "object") { + return reject( + new NotAcceptableException("Invalid token payload"), + ); + } + + if (payload.iis !== "VK") { + return reject( + new NotAcceptableException( + `Unknown issuer, excepted "VK", got "${payload.iis}"`, + ), + ); + } + + if (payload.jti !== 21) { + return reject( + new NotAcceptableException( + `Unknown type, excepted 21, got ${payload.jti}`, + ), + ); + } + + if (payload.app !== vkIdConstants.clientId) { + return reject( + new NotAcceptableException( + `Invalid client_id ${payload.app}`, + ), + ); + } + + resolve(payload); + }); + }); return payload.sub; } diff --git a/src/vkid/vkid.controller.ts b/src/vkid/vkid.controller.ts index a8d0516..4ce4ad1 100644 --- a/src/vkid/vkid.controller.ts +++ b/src/vkid/vkid.controller.ts @@ -38,8 +38,6 @@ export class VKIDController { const result = await this.vkidService.oauth(oAuthRequestDto); if (!result) throw new NotAcceptableException("OAuth process failed"); - console.log("Access token exchanged!", result.accessToken); - return result; } } From 08a5aba41ef0d9d7ca8a1e31b3052eccb604650e Mon Sep 17 00:00:00 2001 From: Nikita <55545594+n08i40k@users.noreply.github.com> Date: Sun, 2 Mar 2025 16:59:39 +0400 Subject: [PATCH 2/2] Create docker-publish.yml --- .github/workflows/docker-publish.yml | 87 ++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/docker-publish.yml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..98c10aa --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,87 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + push: + branches: [ "master" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + +env: + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ secrets.REGISTRY_HOST }} + username: ${{ github.actor }} + password: ${{ secrets.REGISTRY_PASSWD }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ secrets.REGISTRY_HOST }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}