auth0 / auth0-deploy-cli

The Auth0 Deploy CLI is a tool that helps you manage your Auth0 tenant configuration. It integrates into your development workflows as a standalone CLI or as a node module.
MIT License
246 stars 150 forks source link

Allow custom social connection profile script to be included using a file #525

Open DannyPat44 opened 2 years ago

DannyPat44 commented 2 years ago

Describe the problem you'd like to have solved

Within our deployments of Auth0 tenants, we use the Custom Social Connection Type to integrate our different partners as OIDC Identity Providers into our app solutions. As part of the connections, we utilize the custom Fetch User Profile Scripts to fetch and manipulate member details from the partner and our systems.

We attempt to follow the recommendation to keep the scripts simple only making a single request, building the profile and recording any encountered issues to our error tracker. Even with this simplicity the script can be non-trivial and be ~50 lines of code; given the business criticality of the connections, we have a desire to keep the profile scripts under test to minimize the possible issues in production.

With the auth0-deploy-cli tool in both YAML and directory mode this is not currently possible without manual copying and pasting the script directly into either the .yaml or a .json file directly as the script can only be kept in line.

"scripts": { 
   "fetchUserProfile": "function(accessToken, ctx, cb) cb(null, profile);\n  }" 
}
scripts:
        fetchUserProfile: |-
          function(accessToken, ctx, cb) { 
              cb(null, profile);
            }

Describe the ideal solution

We are requesting that the Fetch User Profile Script can reference a .js file in the configuration files (.yaml and .json) that are injected/uploaded at deployment time. Similar to how the Auth0 objects like rules, actions or custom DB scripts work right now. This would allow us to include the script within our testing harness and remove the need for manually copying and pasting the script from a testable file to the configuration files (which could be error-prone).

  - name: Append Backend AT Token Claims
    script: ./rules/append_backend_at_claims.js

Additionally, we currently use the Github actions integration to automatically deploy changes to our different environments which include testing, staging and production. Having the Fetch User Profile Script have the ability to include configuration data similar to rules or custom DB scripts is also desirable. As it would allow us to simplify our deployment workflows and configuration files; currently we need to do some text replacement to ensure we are making requests against the right environment's APIs. For an example see below; we understand that this part of the feature request is likely outside the scope of this repository.

----> tenant.yaml

connections:
  - name: pchealth-staging-ca
    strategy: oauth2
    options:
      scripts:
        fetchUserProfile: ./profile-scripts/embedded_mvp_script.js
      scriptConfigs: 
        - key: "api_domain"
           value: @@TENANT_DOMAIN@@

----> profileScript.js

          async function embeddedMVPProfileScript(accessToken, context, callback) {
            const axios = require("axios");
            const domain = configuration.api_domain;

            let ProfileResponse;
            try {
              leagueProfileResponse = await axios.get(
                `https://rest.${domain}/userinfo`,
              );
            } catch (e) {}

Alternatives and current work-arounds

Currently to deal with this issue manually keeping the script in a file connected to our test harness and once the scripts are created or adjusted manually copying and pasting the script into the .yaml config file inline. Replace placeholder values with environment configuration values using the AUTH0_KEYWORD_REPLACE_MAPPINGS (@@ and ##) tokens to ensure the same template file can be deployed to each of our environments.

We have investigated automating this process through Babel and additional plugins but have decided against investing in doing this due to the complexity and level of effort.

Additional Context

Our testing harness is Jest with custom environment initialization to reflect the Auth0 execution environment. Functions are kept alongside unit tests that execute as part of the CI process. In order to have the rules and objects be testable and uploadable to the Auth0 environment, we run a pre-deployment custom Babel plugin to perform manipulations to adjust the import calls and strip the module.exports plus comments from the referenced functions.

willvedd commented 2 years ago

Thanks for the nicely articulated request. Frankly, it's a perfectly reasonable ask and not the first time this has been requested. At the moment, I'm not sure what the level of effort would be to implement. Main concerns here being backwards compatibility and implementing in a way that's generalized for any code-property, not just the connections scripts.

Despite the idea being good, realistically speaking, we've got our Q2 roadmap for this tool set and can't commit to this feature request at this time.

However, I don't think you need to copy-paste these scripts into your configuration. If paired with another tool like yq or jq, should be doable to mutate the configuration files. As a proof of concept I was able to automate with the below script:

SCRIPT_SRC="$(cat script-you-wish-to-import.js)" yq -i e '.connections[0].options.scripts.fetchUserProfile = strenv(SCRIPT_SRC)' tenant.yaml

Admittedly, this additional step is not ideal and don't officially endorsed, but at least it's another option for you to explore.

DannyPat44 commented 2 years ago

Thank you! For the suggestions! We will investigate this! Understandable about the roadmap.

DannyPat44 commented 2 years ago

@willvedd I investigated the suggestion of adding a step to our Github action workflow to dynamically insert the script using yq. In theory this should work but I am running into a lot of jankiness around having to work around the @@key@@ and ##key## in our Tenant files.

For context for anyone else investigating solutions. We use the Key Word Replacement feature to support our multiple environments. if you do yq will be unable to parse the file because the @@ keys make YAML unparsable. This is a bummer more generally because we haven't been able to implement YAML linters as part of our CI workflows. Additionally, if the ## keys yq are at the start of an array item yq sometimes treats them as comments which will mess up the YAML syntax.

willvedd commented 2 years ago

@DannyPat44 Thanks for the update, sorry it's giving you grief. Is there any way you could provide a small proof-of-concept illustrating the problem in a gist or something? I might be able to help you navigate through some of these messy code issues if I had a way to hack on it myself; I'm optimistic that we could get something working.

Nonetheless, I'll flag this to product so we can evaluate the possibility of tackling in future releases.

DannyPat44 commented 2 years ago

Thanks for the offer of help @willvedd it is very appreciated! I managed to get to a point I am happy with! I have a reusable action workflow as an added step I was able to inject the script with a few constraints/workarounds.

      - name: Optionally insert Profile Script into YAML File
        if: inputs.profile_script != '' && inputs.connection_name != ''
        uses: mikefarah/yq@master
        with:
          cmd: yq -i '(.connections.[] | select(.name=="${{ inputs.connection_name }}") | .options.scripts.fetchUserProfile) = load_str("${{ inputs.profile_script }}")' ${{ inputs.tenant-file }}

The caveat is you can't use the @@ keys in your tenant.yaml file; which is workable by using "##key##" where needed. Avoiding ## also lets us run a YAML validator which is a nice bonus.