Setting up Node.js Integration with Google SAML IDP
Published: 2023-02-27 13:46:28

Security Assertion Markup Language (SAML) is an XML-based standard for exchanging authentication and authorization data between parties, in particular, between an identity provider (IDP) and a service provider (SP). In this article, we will walk through how to set up a Node.js integration with Google SAML IDP using the [SAML Tool](https://www.samltool.com/).

## Overview

The SAML flow allows you to authenticate a user's identity and obtain a SAML response that can be used to interact with Google SAML IDP on their behalf. Here are the steps involved in setting up a Node.js integration with Google SAML IDP:

1. Create a Google Cloud Platform project
2. Set up a SAML app in the Google Cloud Console
3. Configure SAMLTool with your SAML app's settings
4. Use SAMLTool to generate SAML assertion XML
5. Send the SAML assertion to Google for validation

We'll go through each of these steps in detail below.

## Step 1: Create a Google Cloud Platform Project

The first step is to create a Google Cloud Platform project. To do this, follow these steps:

1. Log in to your Google Cloud Platform account
2. Click on the "Select a project" dropdown menu in the top navigation bar
3. Click on "New Project"
4. Fill in the required information and click "Create"

##Step 2: Set up a SAML App in the Google Cloud Console

The next step is to set up a SAML app in the Google Cloud Console. To do this, follow these steps:

1. In the Google Cloud Console, select your project
2. Click on "APIs & Services" and select "Credentials"
3. Click on "Create Credentials" and select "OAuth client ID"
4. Choose "Web application" as the application type
5. Enter a name for your OAuth client ID and enter the authorized redirect URI. This should be the URL where your Node.js app will receive the SAML assertion XML from SAMLTool.
6. Click "Create"
7. In the "Authorized JavaScript origins" field, enter the domain name of your Node.js app.
8. In the "Authorized redirect URIs" field, enter the callback 
URL for your Node.js app.

## Step 3: Configure SAMLTool with your SAML App's Settings

The next step is to configure SAMLTool with your SAML app's settings. To do this, follow these steps:

1. Go to https://www.samltool.com/
2. Click on the "SAML Decoder/Encoder" tab
3. In the "IDP Settings" section, enter the following information:
- Issuer: The Entity ID of your SAML app in the Google Cloud Console
- Single Sign On URL: The SSO URL of your SAML app in the Google Cloud Console
- X.509 Certificate: The public key of your SAML app in the Google Cloud Console
4. In the "NameID Policy" section, select the appropriate NameID format for your SAML app.

## Step 4: Use SAMLTool to Generate SAML Assertion XML

The next step is to use SAMLTool to generate SAML assertion XML. To do this, follow these steps:

1. In the "SAML Decoder/Encoder" tab, select the "Encoder" tab
2. Enter the following information:
- Issuer: The Entity ID of your SAML app in the Google Cloud Console
- NameID: The identifier for the user you want to authenticate
- Audience: The Entity ID of your SAML app in the Google Cloud Console
- Valid for (minutes): The amount of time the SAML assertion will be valid for
- Attributes (optional): Any additional attributes you want to include in the SAML assertion
3. Click "Encode"

SAMLTool will then generate the SAML assertion XML, which you'll use in the next step.

## Step 5: Send the SAML Assertion to Google for Validation

The final step is to send the SAML assertion XML to Google for validation. To do this, follow these steps:

1. In your Node.js app, use the `saml2-js` library to create a SAML request and redirect the user to the SSO URL of your SAML app in the Google Cloud Console.
2. When the user is redirected back to your Node.js app, use the saml2-js library to validate the SAML response from Google.

Here's an example of how to create a SAML request in your Node.js app using the `saml2-js` library:

```
const saml2 = require('saml2-js');

const idp = new saml2.IdentityProvider({
  sso_login_url: '<SSO URL from Google>',
  certificate: '<public key from Google>',
  issuer: '<Entity ID from Google>',
});

const sp_options = {
  entity_id: '<Entity ID from your Node.js app>',
  assertion_consumer_service_url: '<callback URL for your Node.js app>',
  private_key: '<private key for your Node.js app>',
};

const sp = new saml2.ServiceProvider(sp_options);

const options = {
  relay_state: '<relay state>',
  nameid_format: '<NameID format for your SAML app>',
  allow_unencrypted_assertion: true,
};

const request_url = sp.create_login_request_url(idp, options);

res.redirect(request_url);

```

And here's an example of how to validate the SAML response in your Node.js app using the `saml2-js` library:

```
const saml2 = require('saml2-js');

const sp_options = {
  entity_id: '<Entity ID from your Node.js app>',
  private_key: '<private key for your Node.js app>',
};

const sp = new saml2.ServiceProvider(sp_options);

const options = {
  cert: '<public key from Google>',
  audience: '<Entity ID from your Node.js app>',
};

sp.post_assert(options, function(err, saml_response) {
  if (err) {
    console.error(err);
    res.redirect('<error URL>');
  } else {
    console.log(saml_response.user);
    res.redirect('<success URL>');
  }
});

```

Now lets add some unit tests to make sure that we are able to ensure that we have good code coverage.


```
const sinon = require('sinon');
const saml2 = require('saml2-js');

describe('ServiceProvider', () => {
  describe('#post_assert()', () => {
    it('should redirect to the error URL if there is an error', () => {
      const sp_options = {
        entity_id: '<Entity ID from your Node.js app>',
        private_key: '<private key for your Node.js app>',
      };

      const sp = new saml2.ServiceProvider(sp_options);

      const options = {
        cert: '<public key from Google>',
        audience: '<Entity ID from your Node.js app>',
      };

      const callback = sinon.stub();
      const error = new Error('Something went wrong');
      callback.withArgs(error).returns(undefined);

      sp.post_assert(options, callback);
      expect(callback.calledOnce).toBe(true);
    });

    it('should redirect to the success URL if there is no error', () => {
      const sp_options = {
        entity_id: '<Entity ID from your Node.js app>',
        private_key: '<private key for your Node.js app>',
      };

      const sp = new saml2.ServiceProvider(sp_options);

      const options = {
        cert: '<public key from Google>',
        audience: '<Entity ID from your Node.js app>',
      };

      const callback = sinon.stub();
      const saml_response = { user: 'testUser' };
      callback.withArgs(undefined, saml_response).returns(undefined);

      sp.post_assert(options, callback);
      expect(callback.calledOnce).toBe(true);
    });
  });
});
```