UNPKG

@openfga/sdk

Version:

JavaScript and Node.js SDK for OpenFGA

212 lines (211 loc) 9.42 kB
"use strict"; /** * JavaScript and Node.js SDK for OpenFGA * * API version: 1.x * Website: https://openfga.dev * Documentation: https://openfga.dev/docs * Support: https://openfga.dev/community * License: [Apache-2.0](https://github.com/openfga/js-sdk/blob/main/LICENSE) * * NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Credentials = void 0; const axios_1 = require("axios"); const jose = require("jose"); const validation_1 = require("../validation"); const errors_1 = require("../errors"); const common_1 = require("../common"); const types_1 = require("./types"); const attributes_1 = require("../telemetry/attributes"); const counters_1 = require("../telemetry/counters"); const crypto_1 = require("crypto"); class Credentials { static init(configuration, axios = axios_1.default) { return new Credentials(configuration.credentials, axios, configuration.telemetry, configuration.baseOptions); } constructor(authConfig, axios = axios_1.default, telemetryConfig, baseOptions) { this.authConfig = authConfig; this.axios = axios; this.telemetryConfig = telemetryConfig; this.baseOptions = baseOptions; this.initConfig(); this.isValid(); } /** * Sets the default config values * @private */ initConfig() { switch (this.authConfig?.method) { case types_1.CredentialsMethod.ApiToken: if (this.authConfig.config) { if (!this.authConfig.config.headerName) { this.authConfig.config.headerName = "Authorization"; } if (!this.authConfig.config.headerValuePrefix) { this.authConfig.config.headerValuePrefix = "Bearer"; } } break; case types_1.CredentialsMethod.None: default: break; } } /** * * @throws {FgaValidationError} */ isValid() { const { authConfig } = this; switch (authConfig?.method) { case types_1.CredentialsMethod.None: break; case types_1.CredentialsMethod.ApiToken: (0, validation_1.assertParamExists)("Credentials", "config.token", authConfig.config?.token); (0, validation_1.assertParamExists)("Credentials", "config.headerName", authConfig.config?.headerName); (0, validation_1.assertParamExists)("Credentials", "config.headerName", authConfig.config?.headerName); break; case types_1.CredentialsMethod.ClientCredentials: (0, validation_1.assertParamExists)("Credentials", "config.clientId", authConfig.config?.clientId); (0, validation_1.assertParamExists)("Credentials", "config.apiTokenIssuer", authConfig.config?.apiTokenIssuer); (0, validation_1.assertParamExists)("Credentials", "config.apiAudience", authConfig.config?.apiAudience); (0, validation_1.assertParamExists)("Credentials", "config.clientSecret or config.clientAssertionSigningKey", authConfig.config.clientSecret || authConfig.config.clientAssertionSigningKey); if (!(0, validation_1.isWellFormedUriString)(`https://${authConfig.config?.apiTokenIssuer}`)) { throw new errors_1.FgaValidationError(`Configuration.apiTokenIssuer does not form a valid URI (https://${authConfig.config?.apiTokenIssuer})`); } break; } } /** * Get access token, request a new one if not cached or expired * @return string */ async getAccessTokenHeader() { const accessTokenValue = await this.getAccessTokenValue(); switch (this.authConfig?.method) { case types_1.CredentialsMethod.None: return; case types_1.CredentialsMethod.ApiToken: return { name: this.authConfig.config.headerName, value: `${this.authConfig.config.headerValuePrefix ? `${this.authConfig.config.headerValuePrefix} ` : ""}${accessTokenValue}` }; case types_1.CredentialsMethod.ClientCredentials: return { name: "Authorization", value: `Bearer ${accessTokenValue}` }; } } async getAccessTokenValue() { switch (this.authConfig?.method) { case types_1.CredentialsMethod.None: return; case types_1.CredentialsMethod.ApiToken: return this.authConfig.config.token; case types_1.CredentialsMethod.ClientCredentials: if (this.accessToken && (!this.accessTokenExpiryDate || this.accessTokenExpiryDate > new Date())) { return this.accessToken; } return this.refreshAccessToken(); } } /** * Request new access token * @return string */ async refreshAccessToken() { const clientCredentials = this.authConfig?.config; const url = `https://${clientCredentials.apiTokenIssuer}/oauth/token`; const credentialsPayload = await this.buildClientAuthenticationPayload(); try { const wrappedResponse = await (0, common_1.attemptHttpRequest)({ url, method: "POST", data: credentialsPayload, headers: { "Content-Type": "application/x-www-form-urlencoded" } }, { maxRetry: 3, minWaitInMs: 100, }, this.axios); const response = wrappedResponse?.response; if (response) { this.accessToken = response.data.access_token; this.accessTokenExpiryDate = new Date(Date.now() + response.data.expires_in * 1000); } if (this.telemetryConfig?.metrics?.counterCredentialsRequest?.attributes) { let attributes = {}; attributes = attributes_1.TelemetryAttributes.fromRequest({ userAgent: this.baseOptions?.headers["User-Agent"], fgaMethod: "TokenExchange", url, resendCount: wrappedResponse?.retries, httpMethod: "POST", credentials: clientCredentials, start: performance.now(), attributes, }); attributes = attributes_1.TelemetryAttributes.fromResponse({ response, attributes, }); attributes = attributes_1.TelemetryAttributes.prepare(attributes, this.telemetryConfig.metrics?.counterCredentialsRequest?.attributes); this.telemetryConfig.recorder.counter(counters_1.TelemetryCounters.credentialsRequest, 1, attributes); } return this.accessToken; } catch (err) { if (err instanceof errors_1.FgaApiError) { err.constructor = errors_1.FgaApiAuthenticationError; err.name = "FgaApiAuthenticationError"; err.clientId = clientCredentials.clientId; err.audience = clientCredentials.apiAudience; err.grantType = "client_credentials"; } throw err; } } async buildClientAuthenticationPayload() { if (this.authConfig?.method !== types_1.CredentialsMethod.ClientCredentials) { throw new errors_1.FgaValidationError("Credentials method is not set to ClientCredentials"); } const config = this.authConfig.config; if (config.clientAssertionSigningKey) { const alg = config.clientAssertionSigningAlgorithm || "RS256"; const privateKey = await jose.importPKCS8(config.clientAssertionSigningKey, alg); const assertion = await new jose.SignJWT({}) .setProtectedHeader({ alg }) .setIssuedAt() .setSubject(config.clientId) .setJti((0, crypto_1.randomUUID)()) .setIssuer(config.clientId) .setAudience(`https://${config.apiTokenIssuer}/`) .setExpirationTime("2m") .sign(privateKey); return { ...config.customClaims, client_id: config.clientId, client_assertion: assertion, audience: config.apiAudience, client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", grant_type: "client_credentials", }; } else if (config.clientSecret) { return { ...config.customClaims, client_id: config.clientId, client_secret: config.clientSecret, audience: config.apiAudience, grant_type: "client_credentials", }; } throw new errors_1.FgaValidationError("Credentials method is set to ClientCredentials, but no clientSecret or clientAssertionSigningKey is provided"); } } exports.Credentials = Credentials;