Securing Custom Marketing Cloud Apps with JSON Web Tokens (JWT)

Securing Custom Marketing Cloud Apps with JSON Web Tokens (JWT)
Home Page

Securing Custom Marketing Cloud Apps with JSON Web Tokens (JWT)

Introduction

In today's digital landscape, custom apps play a vital role in enhancing the functionality and user experience of marketing cloud platforms. However, with the increasing need for security, it's crucial to implement robust authentication and authorization mechanisms to protect sensitive data and ensure secure interactions between the app and the marketing cloud. JSON Web Tokens (JWT) have emerged as a popular and efficient solution for securing custom apps in marketing cloud environments. This blog will explore the fundamentals of JWT, its benefits, and how to use it effectively to secure custom apps in marketing cloud.

1. Understanding JSON Web Tokens (JWT)

JSON Web Token (JWT) is a compact, URL-safe, and self-contained token format designed to securely transmit information between parties. It is digitally signed and can be verified by the parties involved, ensuring the integrity of the data it carries. JWTs consist of three parts:

  • Header: Contains information about the type of token and the signing algorithm used.
    
    {
      "alg": "HS256",
      "typ": "JWT"
    }
  • Payload: Contains the claims, which are statements about the user or additional data. Claims can include user information, permissions, and other relevant data.
    
    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
    }
  • Signature: Created using the header, payload, and a secret key, the signature ensures the token's integrity and authenticity.
    
    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret)

2. The Benefits of JWT in Marketing Cloud Apps

Implementing JWT in custom marketing cloud apps offers numerous advantages:

  1. Stateless: JWTs are stateless, meaning the server does not need to store user session information. This allows for easy scalability and reduces server-side load.
  2. Cross-platform compatibility: JWT is supported across various programming languages and platforms, making it an ideal choice for heterogeneous marketing cloud environments.
  3. Security: JWTs are digitally signed, ensuring data integrity and protecting against tampering or unauthorized modifications.
  4. Reduced server load: By eliminating the need for session storage, JWTs reduce the burden on the server, leading to improved performance.
  5. Seamless integration: JWTs can be easily integrated into existing marketing cloud app architectures, minimizing the need for significant changes to the current setup.

3. Generating and Verifying JWTs in Marketing Cloud Apps

Let's explore the steps to generate and verify JWTs for securing custom apps in marketing cloud environments:

  1. Step 1: User Authentication
    When a user logs into the custom app, the authentication process takes place. The marketing cloud app's authentication system should validate the user's credentials and generate a JWT as a response.
  2. Step 2: JWT Generation
    The JWT is created by combining the user's claims, a unique secret key known only to the marketing cloud app and the marketing cloud server, and a specified signing algorithm (such as HMAC SHA-256). The resulting JWT is then returned to the user.
  3. Step 3: Token Storage
    Once the user receives the JWT, it can be stored in the app's local storage or as an HTTP-only cookie, depending on the level of security required.
  4. Step 4: API Requests
    For each subsequent API request to the marketing cloud server, the custom app should include the JWT in the request header, usually in the "Authorization" field as a Bearer token.
  5. Step 5: Token Verification
    On the server-side, the marketing cloud platform will receive the JWT in the API request. It will then validate the token's signature using the secret key it shares with the custom app. If the signature is valid, the marketing cloud server can trust the claims within the token and process the API request accordingly.

Conclusion

JSON Web Tokens (JWT) provide an efficient and secure method for safeguarding custom apps in marketing cloud environments. By implementing JWT-based authentication and authorization mechanisms, marketing cloud platforms can enhance the overall security of their custom apps, protect sensitive data, and create a seamless user experience. As marketing technologies continue to evolve, leveraging JWTs for securing custom apps becomes an essential aspect of a successful and trustworthy marketing cloud ecosystem.

Reference:

For more in-depth information about JSON Web Tokens (JWT), you can refer to the official website of JWT.IO. The website provides a comprehensive introduction to JWT, its specifications, use cases, and practical examples.

Website : JWT.IO - Introduction to JSON Web Tokens

By visiting this resource, you can gain a deeper understanding of JWT and its applications, which will help you implement secure authentication and authorization mechanisms for your custom apps in the marketing cloud environment.

Full Code


    <script runat="server">
      // Load the Platform Core library with version 1
      Platform.Load("core", "1");
    
      // Define the name of the Data Extension used to log API exceptions
      var errorLogDEInit = "CustomAppException";
      
      // Main Block: This is the main logic of the script that processes incoming requests and generates responses.
      try {
        // Retrieve the POST data from the request
        var jsonPost = Platform.Request.GetPostData();
        var json = Platform.Function.ParseJSON(jsonPost);
        
        // Initialize the HTTPProperties Data Extension and add a row with request information
        var HTTPPropertiesDE = DataExtension.Init("HTTPProperties");
        HTTPPropertiesDE.Rows.Add({
          Browser: Platform.Request.Browser,
          ClientIP: Platform.Request.ClientIP,
          HasSSL: Platform.Request.HasSSL,
          IsSSL: Platform.Request.IsSSL,
          Method: Platform.Request.Method,
          QueryString: Platform.Request.QueryString,
          ReferrerURL: Platform.Request.ReferrerURL,
          RequestURL: Platform.Request.RequestURL,
          UserAgent: Platform.Request.UserAgent
        });
        
        if (Platform.Request.Method == "GET") {
          // If the HTTP method is GET, return a "403 Forbidden" message
          var response = {
            "message": "403 Forbidden"
          };
        } else {
          switch (json.operation) {
            case "RequestToken":
              // Call the GetAccessToken function to retrieve the access token
              var secret = GetAccessToken(json, errorLogDEInit);
              // Generate the JWT using the GetJWT function
              var jwt = GetJWT(jsonPost, secret, errorLogDEInit);
              // Create the response object with the JWT and token type
              var response = {
                "access_token": jwt,
                "token_type": "Bearer"
              };
              break;
            default:
              // Create an error response object for invalid operations
              var response = {
                "Error": "Invalid operation specified."
              };
          }
        }
        
        // Output the response as a string
        Write(Stringify(response));
      } catch(ex) {
        // Exception handling: Log the error details into the errorLogDEInit Data Extension
        var APIExceptionDE = DataExtension.Init(errorLogDEInit);
        APIExceptionDE.Rows.Add({
          Message: ex.message,
          Description: ex.description,
          InnerException: ex.jintException,
          FunctionName: "Main Block"
        });
        
        // Return an error response as a string in case of an exception
        var response = {
          "Error": ex.message
        };
        Write(Stringify(response));
      }
      
      /**
       * This function retrieves the access token based on the provided credentials.
       * @param {Object} credentials - An object containing the authentication credentials.
       * @param {string} credentials.grant_type - The grant type for authentication.
       * @param {string} credentials.client_id - The client ID for authentication.
       * @param {string} credentials.client_secret - The client secret for authentication.
       * @param {DataExtension} errorLogDEInit - The Data Extension used to log API exceptions.
       * @returns {Object} - An object containing the access token and related information.
       * @throws {Error} - If an exception occurs during execution, it is logged in the errorLogDEInit Data Extension and re-thrown.
       */
      function GetAccessToken(credentials, errorLogDEInit) {
        try {
          // Function logic for GetAccessToken...
          // This function should handle retrieving the access token based on the provided credentials.
          // It may interact with a database or external service to validate the credentials and return the access token.
          // In case of missing or invalid credentials, appropriate error responses should be generated.
          if (!credentials.grant_type || !credentials.client_id || !credentials.client_secret) {
            return {
              "errorcode": 0,
              "message": "missing credentials, make sure you have add grant_type, client id and secret."
            };
          }
          else{
            var appCredentialsDE = DataExtension.Init("AppCredentials");
            var complexfilter = {
              LeftOperand:{
                Property:"client_id",
                SimpleOperator:"equals",
                Value:credentials.client_id
              }
              ,
              LogicalOperator:"AND",
              RightOperand:{
                Property:"client_secret",
                SimpleOperator:"equals",
                Value:credentials.client_secret
              }
            };
            var dataSet= appCredentialsDE.Rows.Retrieve(complexfilter);
            if (!dataSet || dataSet.length === 0) 
            {
              var response={
                "message": "Not Authorized. Please validate your credentials.","code":400};
              return response;
            }
            else{
              
              return (dataSet[0].signing_secret);
            }
          }
          
        } catch (ex) {
          // Exception handling: Log the error details into the errorLogDEInit Data Extension
          var APIExceptionDE = DataExtension.Init(errorLogDEInit);
          APIExceptionDE.Rows.Add({
            Message: ex.message,
            Description: ex.description,
            InnerException: ex.jintException,
            FunctionName: "GetAccessToken"
          });
          throw ex;
        }
      }
    
      /**
       * Generates a JSON Web Token (JWT) from the provided JSON payload using a predefined key and algorithm.
       *
       * @param {string} payload - The JSON payload to be used for generating the JWT.
       * @param {string} signingSecret - The signingSecret to be used for generating the JWT.
       * @param {string} errorLogDEInit - The name of the Data Extension where exceptions will be logged.
       * @returns {string} The generated JWT as a string.
       * @throws {Error} If an exception occurs during JWT generation, it will be caught, logged, and rethrown.
       */
      function GetJWT(payload, signingSecret, errorLogDEInit) {
        try {
          // Function logic for GetJWT...
          // This function should handle generating a JSON Web Token (JWT) from the provided payload using the given signing secret and algorithm.
          // The generated JWT should be returned as a string.
          // In case of any exceptions during JWT generation, appropriate error responses should be generated and logged.
           // Set the value of the "@JSON" variable to the provided JSON payload
    
           Variable.SetValue("@JSON", payload);
          Variable.SetValue("@Secret", signingSecret);
          // Define the AMPscript block to generate the JWT using the GetJWT function
          var ampScriptBlock = "\%\%[SET @JWT = GetJWT(@Secret, 'HS256', @JSON)]\%\%";
          // Process the AMPscript block and execute it
          Platform.Function.TreatAsContent(ampScriptBlock);
          // Return the generated JWT value
          return Variable.GetValue('@JWT');
        } catch (ex) {
          // Exception handling: Log the error details into the errorLogDEInit Data Extension
          var APIExceptionDE = DataExtension.Init(errorLogDEInit);
          APIExceptionDE.Rows.Add({
            Message: ex.message,
            Description: ex.description,
            InnerException: ex.jintException,
            FunctionName: "GetJWT"
          });
          throw ex;
        }
      }
    
      </script>
    


Comments


Knowledge Article

Most Viewed

CLOUD PAGE ENABLEMENT - PART 1

EMAIL NOT SENT IN JOURNEY BUILDER

CONSIDERATIONS FOR JOURNEY BUILDER

Journey Builder REST API Documentation

Preference Center Demystified

Popular Posts

CLOUD PAGE ENABLEMENT - PART 1

EMAIL NOT SENT IN JOURNEY BUILDER

CONSIDERATIONS FOR JOURNEY BUILDER

Journey Builder REST API Documentation

Preference Center Demystified

SEND LOG EANBLEMENT

Share with Friends

Disclaimer:

The information provided on this technical blog is for general informational purposes only. As a SFMC (Salesforce Marketing Cloud) Technical Architect, I strive to offer accurate and up-to-date content related to SFMC and its associated technologies. However, please note that technology is constantly evolving, and the information provided may become outdated or inaccurate over time.

The content published on this blog represents my personal views and experiences as a SFMC Technical Architect and does not necessarily reflect the official views or opinions of any organization or employer I may be affiliated with.

While I make every effort to ensure the accuracy and reliability of the information presented, I cannot guarantee its completeness, suitability, or applicability to your specific circumstances. Therefore, it is essential to verify any information provided and make your own independent assessments or seek professional advice if needed.

Furthermore, any actions taken based on the information provided on this blog are at your own risk. I shall not be held liable for any damages, losses, or inconveniences arising from the use of the information presented here.

Please keep in mind that SFMC and its associated technologies are complex and require technical expertise for proper implementation and management. It is recommended to consult with qualified professionals or official SFMC documentation for comprehensive guidance.

Finally, please note that any product or company names mentioned on this blog are trademarks or registered trademarks of their respective owners. The mention of these trademarks or registered trademarks does not imply any endorsement or affiliation with the blog.

By accessing and using this blog, you agree to the terms of this disclaimer. If you do not agree with any part of this disclaimer, please refrain from using this blog.