import JSON5 from 'json5';
import { isEmpty } from 'lodash';
export const OAUTH_CALLBACK_PATH = 'oauth/callback';
export const LS_OAUTH_DATASOURCE_KEY = 'oauth-datasource';
export const LS_OAUTH_ENV_KEY = 'oauth-env';
export const LS_OAUTH_ERROR_KEY = 'oauth-error';
export const LS_OAUTH_ONE_TIME_CODE = 'oauth-one-time-code';
export const LS_OAUTH_REDIRECT_FINISHED = 'oauth-redirect-finished';
export const LS_FULL_STATE = 'oauth-full-state';
// getAuthId returns the key that is used to identify which token should be used
// for a specific auth type.
// A suffix may be appended for each value that we're caching (eg "-token",
// "-refresh", etc)
export function getAuthId(authType, authConfig, integrationId, integrationConfigurationId) {
    return `${authType}.${getClientId(authType, authConfig, integrationId, integrationConfigurationId)}`;
}
function getClientId(authType, config, integrationId, integrationConfigurationId) {
    const unknownClient = 'unknown-client';
    if (!authType || !config) {
        return unknownClient;
    }
    const clientIdForAuthType = () => {
        switch (authType) {
            case IntegrationAuthType.BASIC:
                return integrationId ?? '';
            case IntegrationAuthType.FIREBASE: {
                const firebaseConfig = config;
                if (!firebaseConfig.apiKey) {
                    return undefined;
                }
                try {
                    const parsed = JSON5.parse(firebaseConfig.apiKey);
                    return parsed.projectId;
                }
                catch {
                    return undefined;
                }
            }
            case IntegrationAuthType.OAUTH2_PASSWORD: {
                return config.clientId;
            }
            case IntegrationAuthType.OAUTH2_CLIENT_CREDS:
            case IntegrationAuthType.OAUTH2_CODE:
            case IntegrationAuthType.OAUTH2_IMPLICIT:
            case GoogleSheetsAuthType.OAUTH2_CODE: {
                const oauthConfig = config;
                let apiId;
                if (normalizeTokenScope(oauthConfig.tokenScope) === TokenScope.DATASOURCE) {
                    apiId = integrationConfigurationId;
                }
                else {
                    apiId = config.clientId;
                }
                // in case there are muliple scopes, separated by a whitespace - sort them before hashing
                const scopeHash = 'scope' in oauthConfig && oauthConfig.scope ? insecureHash(oauthConfig.scope?.split(' ').sort().join(' ')) : null;
                if (!scopeHash || !isEmpty(scopeHash)) {
                    // OAuth scopes are optional according to the OAuth spec.
                    apiId += `-${scopeHash}`;
                }
                return apiId;
            }
            case IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE: {
                const tokenExchangeConfig = config;
                const fieldsToHash = [tokenExchangeConfig.scope?.split(' ').sort().join(' '), tokenExchangeConfig.audience];
                if (tokenExchangeConfig.subjectTokenSource === 'SUBJECT_TOKEN_SOURCE_STATIC_TOKEN') {
                    fieldsToHash.push(tokenExchangeConfig.subjectTokenSourceStaticToken);
                }
                const hashedFields = fieldsToHash.filter(Boolean).map(insecureHash).join('-');
                // clientId is optional for token exchange, so use integrationConfigurationId as fallback
                const identifier = config.clientId || integrationConfigurationId;
                return `${identifier}-${hashedFields}`;
            }
            default:
                return undefined;
        }
    };
    return clientIdForAuthType() ?? unknownClient;
}
// This enum is persisted and used in UI <> agent communication, so take care
// when modifying existing values.
export var IntegrationAuthType;
(function (IntegrationAuthType) {
    IntegrationAuthType["NONE"] = "None";
    IntegrationAuthType["BASIC"] = "basic";
    IntegrationAuthType["OAUTH2_CODE"] = "oauth-code";
    IntegrationAuthType["OAUTH2_CLIENT_CREDS"] = "oauth-client-cred";
    IntegrationAuthType["OAUTH2_IMPLICIT"] = "oauth-implicit";
    IntegrationAuthType["OAUTH2_PASSWORD"] = "oauth-pword";
    IntegrationAuthType["OAUTH2_TOKEN_EXCHANGE"] = "oauth-token-exchange";
    // since proto does not support hyphens
    IntegrationAuthType["OAUTH2_TOKEN_EXCHANGE_PROTO"] = "oauthTokenExchange";
    /** Passes the user's SSO IdP access token through as {{ oauth.token }} — no OAuth client config needed. */
    IntegrationAuthType["OAUTH2_IDP_TOKEN_PASSTHROUGH"] = "oauth-idp-token-passthrough";
    IntegrationAuthType["FIREBASE"] = "Firebase";
    IntegrationAuthType["BEARER"] = "bearer";
    IntegrationAuthType["API_KEY"] = "api-key";
    IntegrationAuthType["TOKEN_PREFIXED"] = "token-prefixed";
    IntegrationAuthType["API_KEY_FORM"] = "api-key-form";
})(IntegrationAuthType || (IntegrationAuthType = {}));
// IntegrationAuthType are old auth types defined in typescript types
// NewAuth Type is defined by proto Auth message
export var NewAuthType;
(function (NewAuthType) {
    NewAuthType["OAUTH2_PASSWORD_GRANT_FLOW"] = "passwordGrantFlow";
    NewAuthType["OAUTH2_AUTH_CODE_FLOW"] = "authorizationCodeFlow";
    NewAuthType["OAUTH2_CLIENT_CREDS_FLOW"] = "clientCredentialsFlow";
    NewAuthType["OAUTH2_TOKEN_EXCHANGE_FLOW"] = "tokenExchangeFlow";
})(NewAuthType || (NewAuthType = {}));
export function isRedirectRequired(authType) {
    switch (authType) {
        case IntegrationAuthType.OAUTH2_CODE:
        case IntegrationAuthType.OAUTH2_IMPLICIT:
            return true;
        default:
            return false;
    }
}
// This enum is used in agent API execution, and API step headers display, so take care
// when modifying existing values.
export const IntegrationAuthHeaderPrefixMap = {
    [IntegrationAuthType.BASIC]: 'Basic ',
    [IntegrationAuthType.BEARER]: 'Bearer ',
    [IntegrationAuthType.OAUTH2_CODE]: 'Bearer ',
    // this is the default prefix for this auth type, but is overridable
    // using the prefix field in the corresponding authConfig
    [IntegrationAuthType.TOKEN_PREFIXED]: 'Bearer '
};
// This enum enumerates the different ways the API Key authentication method can
// propagate the provided key-value pair.
export var ApiKeyMethod;
(function (ApiKeyMethod) {
    ApiKeyMethod["HEADER"] = "header";
    ApiKeyMethod["QUERY_PARAM"] = "query-param";
})(ApiKeyMethod || (ApiKeyMethod = {}));
export var GoogleSheetsAuthType;
(function (GoogleSheetsAuthType) {
    // this is used in GoogleSheetsPlugin.preCreateValidate
    GoogleSheetsAuthType["OAUTH2_CODE"] = "oauth-code";
    GoogleSheetsAuthType["SERVICE_ACCOUNT"] = "service-account";
})(GoogleSheetsAuthType || (GoogleSheetsAuthType = {}));
export var AWSAuthType;
(function (AWSAuthType) {
    AWSAuthType["ACCESS_KEY"] = "access-key";
    AWSAuthType["TOKEN_FILE"] = "token-file";
    AWSAuthType["EC2_INSTANCE_METADATA"] = "ec2-instance-metadata";
})(AWSAuthType || (AWSAuthType = {}));
const awsAuthTypeDisplayName = new Map([
    [AWSAuthType.ACCESS_KEY, 'Access Key'],
    [AWSAuthType.TOKEN_FILE, 'Token File'],
    [AWSAuthType.EC2_INSTANCE_METADATA, 'EC2 Instance Metadata']
]);
export function getAWSAuthTypeDisplayName(authType) {
    return awsAuthTypeDisplayName.get(authType) ?? '';
}
// generally used with authTypeField='connectionType'
var DatabaseAuthType;
(function (DatabaseAuthType) {
    DatabaseAuthType["URL"] = "url";
    DatabaseAuthType["FIELDS"] = "fields";
    DatabaseAuthType["OKTA"] = "okta";
    DatabaseAuthType["KEY_PAIR"] = "key-pair";
})(DatabaseAuthType || (DatabaseAuthType = {}));
export function getDisplayName(authType) {
    switch (authType) {
        case IntegrationAuthType.BASIC:
            return 'Basic Authentication';
        case IntegrationAuthType.FIREBASE:
            return 'Firebase';
        case NewAuthType.OAUTH2_PASSWORD_GRANT_FLOW:
        case IntegrationAuthType.OAUTH2_PASSWORD:
            // APIs should be migrating away from this grant type. Add legacy to hint
            // to users this is probably not the grant type they want.
            return 'OAuth2 - Password Grant (Legacy)';
        case NewAuthType.OAUTH2_CLIENT_CREDS_FLOW:
        case IntegrationAuthType.OAUTH2_CLIENT_CREDS:
            return 'OAuth2 - Client Credentials Grant';
        case IntegrationAuthType.OAUTH2_IMPLICIT:
            return 'OAuth2 - Implicit Grant';
        case NewAuthType.OAUTH2_AUTH_CODE_FLOW:
        case IntegrationAuthType.OAUTH2_CODE:
            return 'OAuth2 - Authorization Code';
        case IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE:
            return 'OAuth2 - On-Behalf-Of Token Exchange';
        case IntegrationAuthType.OAUTH2_IDP_TOKEN_PASSTHROUGH:
            return 'IdP Token Passthrough';
        case IntegrationAuthType.BEARER:
            return 'Bearer Token';
        case IntegrationAuthType.TOKEN_PREFIXED:
            return 'Token';
        case IntegrationAuthType.API_KEY:
            return 'API Key';
        case IntegrationAuthType.API_KEY_FORM:
            return 'API Key';
        case DatabaseAuthType.URL:
            return 'Connection URL';
        case DatabaseAuthType.FIELDS:
            return 'Password-based authentication';
        case DatabaseAuthType.OKTA:
            return 'SSO';
        case DatabaseAuthType.KEY_PAIR:
            return 'Key Pair Authentication';
        case IntegrationAuthType.NONE:
        default:
            return 'None';
    }
}
export var TokenScope;
(function (TokenScope) {
    TokenScope["DATASOURCE"] = "datasource";
    TokenScope["USER"] = "user";
})(TokenScope || (TokenScope = {}));
export var TokenType;
(function (TokenType) {
    TokenType["REFRESH"] = "refresh";
    TokenType["USER"] = "userId";
    // Access token is persisted as "token" for backwards compatibility.
    TokenType["ACCESS"] = "token";
    TokenType["ID"] = "id-token";
})(TokenType || (TokenType = {}));
// normalizeTokenScope coerces a boolean tokenScope value (which can end up in the
// DB when the UI checkbox's mapBooleansTo mapping doesn't fire) to the correct
// TokenScope string enum. This prevents the orchestrator's protojson.Unmarshal
// from rejecting a boolean for the proto string field token_scope.
function normalizeTokenScope(value) {
    if (value === true || value === TokenScope.DATASOURCE)
        return TokenScope.DATASOURCE;
    if (value === false || value === TokenScope.USER)
        return TokenScope.USER;
    if (value === undefined || value === null)
        return undefined;
    if (typeof value === 'string' && Object.values(TokenScope).includes(value))
        return value;
    return undefined;
}
export const extractPublic = (authType, authConfig) => {
    switch (authType) {
        case IntegrationAuthType.BASIC:
            return {
                shareBasicAuthCreds: authConfig?.shareBasicAuthCreds
            };
        case IntegrationAuthType.OAUTH2_CLIENT_CREDS:
            return {
                clientId: authConfig?.clientId,
                tokenUrl: authConfig?.tokenUrl,
                scope: authConfig?.scope
            };
        case IntegrationAuthType.OAUTH2_CODE:
            return {
                clientId: authConfig?.clientId,
                authorizationUrl: authConfig?.authorizationUrl,
                tokenUrl: authConfig?.tokenUrl,
                scope: authConfig?.scope,
                audience: authConfig?.audience,
                refreshTokenFromServer: authConfig?.refreshTokenFromServer,
                hasToken: authConfig?.hasToken,
                tokenScope: normalizeTokenScope(authConfig?.tokenScope),
                promptType: authConfig?.promptType,
                sendOAuthState: authConfig?.sendOAuthState
            };
        case IntegrationAuthType.OAUTH2_IMPLICIT:
            return {
                clientId: authConfig?.clientId,
                authorizationUrl: authConfig?.authorizationUrl,
                scope: authConfig?.scope,
                audience: authConfig?.audience
            };
        case IntegrationAuthType.OAUTH2_PASSWORD:
            return {
                clientId: authConfig?.clientId,
                tokenUrl: authConfig?.tokenUrl,
                useFixedPasswordCreds: authConfig?.useFixedPasswordCreds,
                audience: authConfig?.audience
            };
        case IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE:
            return {
                clientId: authConfig?.clientId,
                tokenUrl: authConfig?.tokenUrl,
                audience: authConfig?.audience,
                scope: authConfig?.scope,
                subjectTokenSource: authConfig?.subjectTokenSource,
                subjectTokenSourceStaticToken: authConfig?.subjectTokenSourceStaticToken
            };
        case IntegrationAuthType.FIREBASE:
            return authConfig ?? {};
        case IntegrationAuthType.NONE:
            return authConfig ?? {};
        case GoogleSheetsAuthType.OAUTH2_CODE:
        case GoogleSheetsAuthType.SERVICE_ACCOUNT:
        case IntegrationAuthType.BEARER:
        case IntegrationAuthType.TOKEN_PREFIXED:
        case IntegrationAuthType.API_KEY:
        case IntegrationAuthType.API_KEY_FORM:
        case IntegrationAuthType.OAUTH2_IDP_TOKEN_PASSTHROUGH:
        case DatabaseAuthType.FIELDS:
        case DatabaseAuthType.OKTA:
        case DatabaseAuthType.KEY_PAIR:
        case DatabaseAuthType.URL:
            return {};
        default:
            throw new Error(`unknown auth type: ${authType}`);
    }
};
// TODO: this lives here for now to avoid a cyclic dependency. The utils
// directory transitively depends on this file.
// An insecure hash function used to condense a string.
const insecureHash = (s) => {
    // We need the redundant `!s` check to satisfy the type checker that s is not
    // undefined.
    if (!s || isEmpty(s)) {
        return '';
    }
    let hash = 0;
    for (let i = 0; i < s.length; i++) {
        const char = s.charCodeAt(i);
        hash = (hash << 5) - hash + char;
        // Convert to 32 bits.
        hash = hash & hash;
    }
    return hash.toString();
};
export function isExchangeCodeResponse(obj) {
    return 'successful' in obj;
}
export function isDatasourceAuthState(aus) {
    return ('integrationId' in aus && 'configurationId' in aus && 'authType' in aus && 'authId' in aus && 'authConfig' in aus && 'origin' in aus);
}
//# sourceMappingURL=auth.js.map