"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. Object.defineProperty(exports, "__esModule", { value: true }); exports.TunnelAccessTokenProperties = void 0; /** * Supports parsing tunnel access token JWT properties to allow for some pre-validation * and diagnostics. * * Applications generally should not attempt to interpret or rely on any token properties * other than `expiration`, because the service may change or omit those claims in the future. * Other claims are exposed here only for diagnostic purposes. */ class TunnelAccessTokenProperties { constructor(clusterId, tunnelId, tunnelPorts, scopes, issuer, expiration) { this.clusterId = clusterId; this.tunnelId = tunnelId; this.tunnelPorts = tunnelPorts; this.scopes = scopes; this.issuer = issuer; this.expiration = expiration; } toString() { let s = ''; if (this.tunnelId) { s += 'tunnel='; s += this.tunnelId; if (this.clusterId) { s += '.'; s += this.clusterId; } } if (this.tunnelPorts && this.tunnelPorts.length > 0) { if (s.length > 0) s += ', '; if (this.tunnelPorts.length === 1) { s += `port=${this.tunnelPorts[0]}`; } else { s += `ports=[${this.tunnelPorts.join(', ')}]`; } } if (this.scopes) { if (s.length > 0) s += ', '; s += `scopes=[${this.scopes.join(', ')}]`; } if (this.issuer) { if (s.length > 0) s += ', '; s += 'issuer='; s += this.issuer; } if (this.expiration) { if (s.length > 0) s += ', '; s += `expiration=${this.expiration.toString().replace('.000Z', 'Z')}`; } return s; } /** * Checks if the tunnel access token expiration claim is in the past. * * (Does not throw if the token is an invalid format.) */ static validateTokenExpiration(token) { const t = TunnelAccessTokenProperties.tryParse(token); if (t === null || t === void 0 ? void 0 : t.expiration) { if (t.expiration < new Date()) { throw new Error('The access token is expired: ' + t); } } } /** * Attempts to parse a tunnel access token (JWT). This does NOT validate the token * signature or any claims. */ static tryParse(token) { if (typeof token !== 'string') throw new TypeError('Token string expected.'); // JWTs are encoded in 3 parts: header, body, and signature. const tokenParts = token.split('.'); if (tokenParts.length !== 3) { return null; } const tokenBodyJson = TunnelAccessTokenProperties.base64UrlDecode(tokenParts[1]); if (!tokenBodyJson) { return null; } try { const tokenJson = JSON.parse(tokenBodyJson); const clusterId = tokenJson.clusterId; const tunnelId = tokenJson.tunnelId; const ports = tokenJson.tunnelPorts; const scp = tokenJson.scp; const iss = tokenJson.iss; const exp = tokenJson.exp; return new TunnelAccessTokenProperties(clusterId, tunnelId, typeof ports === 'number' ? [ports] : ports, scp === null || scp === void 0 ? void 0 : scp.split(' '), iss, typeof exp === 'number' ? new Date(exp * 1000) : undefined); } catch (_a) { return null; } } /** * Gets the tunnal access token trace string. * 'none' if null or undefined, parsed token info if can be parsed, or 'token' if cannot be parsed. */ static getTokenTrace(token) { var _a, _b; return !token ? 'none' : (_b = (_a = TunnelAccessTokenProperties.tryParse(token)) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : 'token'; } /** * Gets a tunnel access token that matches any of the provided access token scopes. * Validates token expiration if the token is found and throws an error if it's expired. * @param tunnel The tunnel to get the access tokens from. * @param accessTokenScopes What scopes the token needs to have. * @returns Tunnel access token if found; otherwise, undefined. */ static getTunnelAccessToken(tunnel, accessTokenScopes) { if (!(tunnel === null || tunnel === void 0 ? void 0 : tunnel.accessTokens) || !accessTokenScopes) { return; } if (!Array.isArray(accessTokenScopes)) { accessTokenScopes = [accessTokenScopes]; } for (const scope of accessTokenScopes) { for (const [key, accessToken] of Object.entries(tunnel.accessTokens)) { // Each key may be either a single scope or space-delimited list of scopes. if (accessToken && key.split(' ').includes(scope)) { TunnelAccessTokenProperties.validateTokenExpiration(accessToken); return accessToken; } } } } static base64UrlDecode(encodedString) { // Convert from base64url encoding to base64 encoding: replace chars and add padding. encodedString = encodedString.replace('-', '+'); while (encodedString.length % 4 !== 0) { encodedString += '='; } try { const result = atob(encodedString); return result; } catch (_a) { return null; } } } exports.TunnelAccessTokenProperties = TunnelAccessTokenProperties; //# sourceMappingURL=tunnelAccessTokenProperties.js.map