diff --git a/src/api/routes/auth.js b/src/api/routes/auth.js index 494fe68..37c7f5a 100644 --- a/src/api/routes/auth.js +++ b/src/api/routes/auth.js @@ -1,9 +1,11 @@ import { Router } from "express"; +import axios from "axios"; import { logger } from "../../utils/index.js"; import { decode } from "jsonwebtoken"; import jwt from "jsonwebtoken"; import base64url from "base64url"; import jwkToPem from "jwk-to-pem"; +import { sendHTTPError } from "../../utils/http.js"; const router = Router(); @@ -17,44 +19,57 @@ router.post("/", (req, res) => { logger.debug(JSON.stringify(req.body, null, 2)); var authHeader = req.headers.authorization; const prefix = "OIDC_id_token "; - if (authHeader.startsWith(prefix)) { + if (authHeader && authHeader.startsWith(prefix)) { var token = authHeader.substring(prefix.length, authHeader.length); // base64 should be URL encoded and padding should be removed. token = base64url.fromBase64(token.replace(/=/g, "")); - const jwk = { - alg: "RS256", - e: "AQAB", - kid: "3818ed312ed8da55defd3a3fb48f56431ac1c02a3f6d2ad25c409fa0905457e9", - kty: "RSA", - n: "AKHh-mvG1TFYBn0nxSFPtU0PEG7fd27MGRWG92nP_FSXyLyMyIeUxKUWg5t1-2wK2ue8Z3lq8G9YqnoHfTmnKDT_zLESlKlAgx9KNeumedEQu18KyoGXK9eqnwNkV05sWGqMN4OXzp3s88o07ni2KDXiEv4UTBJP44VPDQlrophYNxA0H_BpedXUqd8J0hSheDwL_b_lktZDZB2UrgspadGPAsLLM7DRajmzB8sGXe0TZSD0jB2YnJAJZrNKzAbDeRPY4kHwt23_uM3Sa-cQe_mfQY1jkBJkz6ullCU-8twD3p3Ckdeq1g5duCD0vqPPXn5OCP8DsRpziOQSlv9p7c0=", - use: "sig", - }; - - var pem = jwkToPem(jwk); - - try { - const verified = jwt.verify(token, pem, { algorithms: ["RS256"] }); - logger.debug(`Verified [${JSON.stringify(verified, null, 2)}]`); - } catch (err) { - logger.error( - `verify failed [${JSON.stringify( - err, - null, - 2 - )}]. This is most likely because we modified the token to be URL Base64 compatible during processing` - ); - } - const decoded = decode(token); logger.debug(`Decoded [${JSON.stringify(decoded, null, 2)}]`); - res.send(decoded); - return; + + if (decoded.iss) { + const jwkURI = decoded.iss + "/connect/jwk_uri"; + + logger.debug(`Requesting JWK on ${jwkURI}`); + + axios + .get(jwkURI) + .then((jwkResponse) => { + logger.debug( + `Response JWK on ${JSON.stringify(jwkResponse.data, null, 2)}` + ); + + if ( + jwkResponse.data && + jwkResponse.data.keys && + jwkResponse.data.keys.length > 0 + ) { + const key = jwkResponse.data.keys[0]; + var pem = jwkToPem(key); + + try { + const verified = jwt.verify(token, pem, { + algorithms: [key.alg], + }); + logger.debug(`Verified [${JSON.stringify(verified, null, 2)}]`); + res.send(decoded); + } catch (err) { + logger.error(`Verify failed [${JSON.stringify(err, null, 2)}].`); + sendHTTPError(err, res); + } + } + }) + .catch((err) => { + logger.error(`JWK Request failed [${JSON.stringify(err, null, 2)}].`); + sendHTTPError(err, res); + }); + } } else { - //Error + sendHTTPError( + new Error("No OIDC_id_token found in Authentication header.") + ); } - res.send({}); }); export default router; diff --git a/src/utils/http.js b/src/utils/http.js new file mode 100644 index 0000000..54527d2 --- /dev/null +++ b/src/utils/http.js @@ -0,0 +1,13 @@ +export function sendHTTPError(err, res) { + let errStatus = 500; + if (err.response && err.response.status) errStatus = err.response.status; + res.status(errStatus).send({ + errors: [ + { + status: errStatus, + title: err.code, + detail: err.message, + }, + ], + }); +}