feat(auth): adopt @siax/auth-zitadel canonical claim normalizer #475
+3
-2
@@ -52,6 +52,8 @@
|
||||
"@radix-ui/react-toggle-group": "1.1.19",
|
||||
"@radix-ui/react-tooltip": "1.2.16",
|
||||
"@siax/0pt-decision": "workspace:*",
|
||||
"@siax/access": "workspace:*",
|
||||
"@siax/auth-zitadel": "0.1.1",
|
||||
"@siax/charts": "^0.5.0",
|
||||
"@siax/collaboration": "0.3.0",
|
||||
"@siax/config": "^0.2.1",
|
||||
@@ -81,8 +83,7 @@
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "3.25.76",
|
||||
"@siax/access": "workspace:*"
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.15.0",
|
||||
|
||||
Generated
+15
@@ -108,6 +108,9 @@ importers:
|
||||
'@siax/access':
|
||||
specifier: workspace:*
|
||||
version: link:packages/siax-access
|
||||
'@siax/auth-zitadel':
|
||||
specifier: 0.1.1
|
||||
version: 0.1.1
|
||||
'@siax/charts':
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
||||
@@ -1549,6 +1552,12 @@ packages:
|
||||
'@siax/analytical-context@0.5.1':
|
||||
resolution: {integrity: sha512-bqc2NGPyzLVcu4VUgz1XYsyghSM4c3ck5gBVFSzeRYMhe2cj0nf5Fm5n/sFaOuhMaKdDnX/TIR2FO6f21lgufg==, tarball: https://git.siax.io/api/packages/sax3l/npm/%40siax%2Fanalytical-context/-/0.5.1/analytical-context-0.5.1.tgz}
|
||||
|
||||
'@siax/auth-core@0.1.0':
|
||||
resolution: {integrity: sha512-oTfEO4S6pv3rQ6bSh44+fRWPiwGr9nYCf6uWSBF8g5PFUspW6nr4wKQZVAkLNwfC0Pu6o+CH8ltsuu+fY8gOQA==, tarball: https://git.siax.io/api/packages/sax3l/npm/%40siax%2Fauth-core/-/0.1.0/auth-core-0.1.0.tgz}
|
||||
|
||||
'@siax/auth-zitadel@0.1.1':
|
||||
resolution: {integrity: sha512-SmoBswG5P7szZIaETqmo1NN5RhdXLq2RDR/1wObfxHM2yUEXNSZQJznZoAb8z3lm/PvBgQhmnHiOHCfidN1ZQg==, tarball: https://git.siax.io/api/packages/sax3l/npm/%40siax%2Fauth-zitadel/-/0.1.1/auth-zitadel-0.1.1.tgz}
|
||||
|
||||
'@siax/charts@0.11.0':
|
||||
resolution: {integrity: sha512-IQnby+h6jVMaRAM7DcfUMuMbSUjd8B3+AGk9N4pXk2GDudMRMjt0bTtu75637Jc1Mf4iNonvgp8ytKlS7KdW4A==, tarball: https://git.siax.io/api/packages/sax3l/npm/%40siax%2Fcharts/-/0.11.0/charts-0.11.0.tgz}
|
||||
peerDependencies:
|
||||
@@ -4236,6 +4245,12 @@ snapshots:
|
||||
|
||||
'@siax/analytical-context@0.5.1': {}
|
||||
|
||||
'@siax/auth-core@0.1.0': {}
|
||||
|
||||
'@siax/auth-zitadel@0.1.1':
|
||||
dependencies:
|
||||
'@siax/auth-core': 0.1.0
|
||||
|
||||
'@siax/charts@0.11.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)':
|
||||
dependencies:
|
||||
'@siax/analytical-context': 0.5.1
|
||||
|
||||
+25
-8
@@ -50,6 +50,7 @@ import {
|
||||
type JWTPayload,
|
||||
type JWTVerifyGetKey,
|
||||
} from 'jose';
|
||||
import { normalizeZitadelClaims, ZitadelClaimError } from '@siax/auth-zitadel';
|
||||
|
||||
/** Default fleet Zitadel issuer (OIDC). Overridable via ZITADEL_ISSUER. */
|
||||
export const DEFAULT_ZITADEL_ISSUER = 'https://id-customers.siax.io';
|
||||
@@ -254,13 +255,29 @@ export async function verifyBearer(
|
||||
throw new BearerVerificationError('token_verification_failed', err);
|
||||
}
|
||||
|
||||
const sub = typeof payload.sub === 'string' ? payload.sub : undefined;
|
||||
if (!sub || sub.length === 0) {
|
||||
throw new BearerVerificationError('token_missing_sub');
|
||||
try {
|
||||
const identity = normalizeZitadelClaims(payload, {
|
||||
source: 'jwt',
|
||||
tenantClaim: opts.tenantClaim ?? tenantClaimOverride(),
|
||||
});
|
||||
return {
|
||||
sub: identity.principal.subject,
|
||||
tenantId: identity.tenant.tenantId,
|
||||
claims: payload,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof ZitadelClaimError) {
|
||||
if (/sub/i.test(err.reason)) {
|
||||
throw new BearerVerificationError('token_missing_sub');
|
||||
}
|
||||
if (/tenant/i.test(err.reason)) {
|
||||
throw new BearerVerificationError('token_missing_tenant_claim');
|
||||
}
|
||||
if (/aud/i.test(err.reason)) {
|
||||
throw new BearerVerificationError('token_invalid_audience');
|
||||
}
|
||||
throw new BearerVerificationError('token_verification_failed', err);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
const tenantId = deriveTenantId(payload, opts.tenantClaim ?? tenantClaimOverride());
|
||||
if (!tenantId) {
|
||||
throw new BearerVerificationError('token_missing_tenant_claim');
|
||||
}
|
||||
return { sub, tenantId, claims: payload };
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ async function sign(claims: Record<string, unknown>, opts: SignOpts = {}): Promi
|
||||
.setProtectedHeader({ alg: 'RS256', kid: KID })
|
||||
.setIssuedAt()
|
||||
.setIssuer(opts.issuer ?? ISSUER)
|
||||
.setAudience('siax-test')
|
||||
.setExpirationTime(opts.expirationTime ?? '2h')
|
||||
.sign(privateKey);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user