diff --git a/backend/src/lib/template/dot-access.ts b/backend/src/lib/template/dot-access.ts index ec3208feb..898252acc 100644 --- a/backend/src/lib/template/dot-access.ts +++ b/backend/src/lib/template/dot-access.ts @@ -1,11 +1,11 @@ /** * Safely retrieves a value from a nested object using dot notation path */ -export const getStringValueByDot = ( +export const getValueByDot = ( obj: Record | null | undefined, path: string, - defaultValue?: string -): string | undefined => { + defaultValue?: string | number | boolean +): string | number | boolean | undefined => { // Handle null or undefined input if (!obj) { return defaultValue; @@ -26,7 +26,7 @@ export const getStringValueByDot = ( current = (current as Record)[part]; } - if (typeof current !== "string") { + if (typeof current !== "string" && typeof current !== "number" && typeof current !== "boolean") { return defaultValue; } diff --git a/backend/src/services/identity-jwt-auth/identity-jwt-auth-service.ts b/backend/src/services/identity-jwt-auth/identity-jwt-auth-service.ts index 7b0a19414..9cc851437 100644 --- a/backend/src/services/identity-jwt-auth/identity-jwt-auth-service.ts +++ b/backend/src/services/identity-jwt-auth/identity-jwt-auth-service.ts @@ -21,7 +21,7 @@ import { UnauthorizedError } from "@app/lib/errors"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; -import { getStringValueByDot } from "@app/lib/template/dot-access"; +import { getValueByDot } from "@app/lib/template/dot-access"; import { ActorType, AuthTokenType } from "../auth/auth-type"; import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; @@ -189,7 +189,7 @@ export const identityJwtAuthServiceFactory = ({ if (identityJwtAuth.boundClaims) { Object.keys(identityJwtAuth.boundClaims).forEach((claimKey) => { const claimValue = (identityJwtAuth.boundClaims as Record)[claimKey]; - const value = getStringValueByDot(tokenData, claimKey) || ""; + const value = getValueByDot(tokenData, claimKey); if (!value) { throw new UnauthorizedError({ @@ -198,9 +198,7 @@ export const identityJwtAuthServiceFactory = ({ } // handle both single and multi-valued claims - if ( - !claimValue.split(", ").some((claimEntry) => doesFieldValueMatchJwtPolicy(tokenData[claimKey], claimEntry)) - ) { + if (!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchJwtPolicy(value, claimEntry))) { throw new UnauthorizedError({ message: `Access denied: claim mismatch for field ${claimKey}` }); diff --git a/backend/src/services/identity-oidc-auth/identity-oidc-auth-fns.ts b/backend/src/services/identity-oidc-auth/identity-oidc-auth-fns.ts index 7d386afcb..8ed134cba 100644 --- a/backend/src/services/identity-oidc-auth/identity-oidc-auth-fns.ts +++ b/backend/src/services/identity-oidc-auth/identity-oidc-auth-fns.ts @@ -1,7 +1,16 @@ import picomatch from "picomatch"; -export const doesFieldValueMatchOidcPolicy = (fieldValue: string, policyValue: string) => - policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue); +export const doesFieldValueMatchOidcPolicy = (fieldValue: string | number | boolean, policyValue: string) => { + if (typeof fieldValue === "boolean") { + return fieldValue === (policyValue === "true"); + } + + if (typeof fieldValue === "number") { + return fieldValue === parseInt(policyValue, 10); + } + + return policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue); +}; export const doesAudValueMatchOidcPolicy = (fieldValue: string | string[], policyValue: string) => { if (Array.isArray(fieldValue)) { diff --git a/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts b/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts index 617b21a1f..6585e61f3 100644 --- a/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts +++ b/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts @@ -22,7 +22,7 @@ import { UnauthorizedError } from "@app/lib/errors"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; -import { getStringValueByDot } from "@app/lib/template/dot-access"; +import { getValueByDot } from "@app/lib/template/dot-access"; import { ActorType, AuthTokenType } from "../auth/auth-type"; import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; @@ -146,7 +146,7 @@ export const identityOidcAuthServiceFactory = ({ if (identityOidcAuth.boundClaims) { Object.keys(identityOidcAuth.boundClaims).forEach((claimKey) => { const claimValue = (identityOidcAuth.boundClaims as Record)[claimKey]; - const value = getStringValueByDot(tokenData, claimKey) || ""; + const value = getValueByDot(tokenData, claimKey); if (!value) { throw new UnauthorizedError({ @@ -167,13 +167,13 @@ export const identityOidcAuthServiceFactory = ({ if (identityOidcAuth.claimMetadataMapping) { Object.keys(identityOidcAuth.claimMetadataMapping).forEach((permissionKey) => { const claimKey = (identityOidcAuth.claimMetadataMapping as Record)[permissionKey]; - const value = getStringValueByDot(tokenData, claimKey) || ""; + const value = getValueByDot(tokenData, claimKey); if (!value) { throw new UnauthorizedError({ message: `Access denied: token has no ${claimKey} field` }); } - filteredClaims[permissionKey] = value; + filteredClaims[permissionKey] = value.toString(); }); }