Firma de tokens jwt propios para las solicitues

This commit is contained in:
2026-01-27 12:12:40 +01:00
parent 4cf7d11649
commit e8c278aa41
7 changed files with 125 additions and 9 deletions

View File

@@ -1,3 +1,5 @@
import { sign } from "node:crypto"
export type JWTHeader = {
alg: string,
typ: string,
@@ -42,6 +44,20 @@ export type JWT<T> = {
signature: JWTSignature
}
// todo pasar a la clase JWT
function signJWT(args: {
algorythm: "sha256" | string,
data: string,
privateKey: string
}) {
const signature = sign(
args.algorythm,
Buffer.from(args.data),
args.privateKey
)
return signature
}
export class JWTToken<T> {
public rawToken: string
@@ -54,6 +70,37 @@ export class JWTToken<T> {
this.decodedPayload = this.decodePayload()
}
public static fromParts<T>(args: {
header: JWTHeader,
payload?: JWTPayload<T>,
sigantureData?: {
privateKey: string,
algorythm: string
}
}) {
const strHeader = JSON.stringify(args.header)
const base64Header = Buffer.from(strHeader).toString("base64url")
let msg = base64Header
if (args.payload != undefined) {
const strPayload = JSON.stringify(args.payload)
const base64payload = Buffer.from(strPayload).toString("base64url")
msg += ("." + base64payload)
}
if (args.sigantureData != undefined) {
const base64signature = signJWT({
algorythm: args.sigantureData.algorythm,
privateKey: args.sigantureData.privateKey,
data: msg
}).toString("base64url")
msg += ("." + base64signature)
}
console.log("JWT", msg)
}
private decodePayload(): JWTPayload<T> {
if (this.rawToken == undefined) throw new Error("La clase no tiene un token definido")
const rawTokenPayload = this.rawToken.split(".")[1]

View File

@@ -3,3 +3,4 @@ OBJ_PEM_PATH=./obj.pem
OBJ_AUTHORIZATION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSIxi8nOcTg7r_LM_nmd5qm7uLbksf_XArjI8iAyhjKz_2BAXPhmvKs4Fc9f3vv5LDfCVrPB9lP8P7rJ66_qnWs4jvhLQxSfn29m96hgXeCf8oySdIDUjN2q9Js3KAS5LL52Ri6ryvUeO1PvMhaPQMWRqoHIqTV1wPfPtiqQwcjUPmu5GeW164Kq1JLgV3KaGzfCZ9Qv9lbv30EJrukXxWuLCAhBS0kzrBXZoWvf2pb9uh3Am_93_dDxiIGQfIap9ZU_m8ZD1HPgvZOMCY6ZkxQconQ
OBJ_CLI_ASSERTION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSIxi8nOcTg7r_LM_nmd5qm7uLbksf_XArjI8iAyhjKz_2BAXPhmvKs4Fc9f3vv5LDfCVrPB9lP8P7rJ66_qnWs4jvhLQxSfn29m96hgXeCf8oySdIDUjN2q9Js3KAS5LL52Ri6ryvUeO1PvMhaPQMWRqoHIqTV1wPfPtiqQwcjUPmu5GeW164Kq1JLgV3KaGzfCZ9Qv9lbv30EJrukXxWuLCAhBS0kzrBXZoWvf2pb9uh3Am_93_dDxiIGQfIap9ZU_m8ZD1HPgvZOMCY6ZkxQconQ
OBJ_CLIENT_ID=savefamily_rest_ws
OBJ_KID=xNfbMiyL1ORXGP8lElhcv8nVaG3EJKye4Lc1YoN3I1E

View File

@@ -1,6 +1,5 @@
import { test, describe } from "vitest"
import { JWTService } from "./JWT.service"
import { loadEnvFile } from "node:process"
describe("Tokens Objenious", () => {
const jwtService = new JWTService()
@@ -9,7 +8,7 @@ describe("Tokens Objenious", () => {
console.log("test env", process.env.OBJ_CLIENT_ID)
test("Solicicitud normal de auth", async () => {
//const token = await jwtService.getAccessToken()
const token = await jwtService.getAccessToken()
console.log("acceso objenious", token)
})
})

View File

@@ -1,11 +1,13 @@
// PEM ?
import { env } from "#config/env";
import fs from "fs"
import {
JWTToken
} from "#shared/domain/JWT"
import axios from "axios";
import axios, { AxiosError } from "axios";
import { sign } from "node:crypto"
type GrantAccessRequestBody = {
grant_type: string,
@@ -23,9 +25,19 @@ type TokensRequestResponse = {
"not-before-policy": number,
"session_state": string,
"scope": string
}
type AuthHeaders = {
content_type: string,
sub: string,
iss: string,
aud: string,
jti: string,
iat: number,
exp: number,
}
const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token"
const REFRESH_TOKEN_URL = GET_TOKEN_URL
@@ -45,6 +57,20 @@ const DEFAULT_HEADERS = {
"content-type": "application/x-www-form-urlencoded"
}
function addIATHeaders(authHeaders: Object) {
const headers = <AuthHeaders>{
...authHeaders,
sub: env.OBJ_CLIENT_ID,
iss: env.OBJ_CLIENT_ID,
aud: GET_TOKEN_URL,
jti: Date.now().toString(),
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 5 * 60,
}
return headers
}
/**
* El servicio gestiona un par de tokens auth - refresh para las
* operaciones de Objenious.
@@ -69,12 +95,40 @@ export class JWTService {
console.warn("Se está intentado conseguir un token sin expirar el anterior")
}
console.log("headers", addIATHeaders(DEFAULT_HEADERS))
console.log("body", DEFAULT_BODY)
console.log("keypath", __dirname + "/../obj.pem")
const key = fs.readFileSync(__dirname + "/../obj.pem", "utf8")
const msg = Buffer.from("test")
const signature = sign(
"sha256",
Buffer.from(msg),
key
)
JWTToken.fromParts({
header: { alg: "RS256", typ: "JWT", kid: "1234" },
payload: {
"iss": "savefamily_rest_ws",
"aud": "https://idp.docapost.io/auth/realms/GETWAY",
},
sigantureData: {
algorythm: "sha256",
privateKey: key
}
})
console.log("signature", signature.toString("base64url"))
return;
const req = axios.post(GET_TOKEN_URL,
DEFAULT_BODY,
{
headers: DEFAULT_HEADERS
headers: addIATHeaders(DEFAULT_HEADERS)
}
)
let res;
try {
@@ -84,7 +138,7 @@ export class JWTService {
return this.authToken
} catch (e) {
const errorString = "No se ha podido conseguir el token de acceso de OBJENIOUS"
console.error(errorString, e)
console.error(errorString, (e as AxiosError).response?.data)
throw new Error(errorString)
}

View File

@@ -26,8 +26,8 @@ export const env = {
OBJ_PEM_PATH: String(process.env.OBJ_PEM_PATH),
OBJ_AUTHOIZATION: String(process.env.OBJ_ATHORIZATION),
OBJ_CLI_ASSERTION: String(process.env.OBJ_CLI_ASSERTION),
OBJ_CLIENT_ID: String(process.env.OBJ_CLIENT_ID)
OBJ_CLIENT_ID: String(process.env.OBJ_CLIENT_ID),
OBJ_KID: String(process.env.OBJ_KID)
};
console.log("env", env)

View File

@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIeBiNCOYvDODs8ruiJzTAiBgW
IiYbKdJf5fyX56tUhXhSHqG53nK0U9xgBFZY2y3DBXNnE9Sl9lXYdAo8kULtXTT4
3Krcwb1mmbg3ofVliyGaLSymtSJGHfTKG2VMUf6EBkjnNKn279h7xnK+xvKb7KrK
vNPaK2KZllMX7ITRnwIDAQAB
-----END PUBLIC KEY-----

View File

@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4aJ+D7qw+PI0Pa/0BKz4
1Y6ihP7FLBBag93KPeo1SEjinXyVTfFmxeTxOEhUvsomIA1E5QounKD6i3H83bX/
+KMm0GgpT+9OjbkEWF8a7+kMGXokilE/9Ah9uwBBzXCzJl4YUyE2VhDQUG6nYCj2
0XH8bSbtdEb5cUeFA6ZTMm3wYddmfJjKTtZvz4yiyLO8HjW2+1H32rQ0Sr0d7mPD
ch0zm4kBjG4kVXgSUnfk68TgUVNTwikxEQfJ5fb+AME0HOA9mgiGdyKgdNIsd4IP
2zgenWpniAPbGfth6sO78+vrETw/Ul42dgxpxejLfgDhqgrTPbi7zFomh61WFVJO
zQIDAQAB
-----END PUBLIC KEY-----