> For the complete documentation index, see [llms.txt](https://docs.fortifiedid.se/use-cases/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fortifiedid.se/use-cases/misc/google/protect-google-workspace-with-eid-mfa.md).

# Protect Google Workspace with eID MFA

## Scenario

In this use case there is one scenario:

* **MFA authentication to Google Workspace.**\
  In this scenario you will login using your BankID.\
  Integrity web will perform a lookup against Google to fetch the user object, using the identifier from the eID as key (in this scenario the social security number from the BankID eID).\
  The primary email value will be taken from the user object and added to the token (SAML assertion) which will be passed back to Google for user verification.

Google Workspace is a set of tools for organizations provided by Google, such as mail, drive, classroom, education and more.

**This scenario could easily be copied and modified to fulfill:**

* **Using other eID methods to authenticate to Google Workspace, such as SITHS, EFOS, Freja, Norwegian ID-porten, Foreign eID (eIDAS), eduID, Suomi.fi.**

## Prerequisite

* FortifiedID **Integrity Web** current version installed
* **BankID certificate**. To be able to communicate with bankid backend.
* **Google Workspace administration rights.**
* **Host (DNS) name** of the Integrity service (external access)
* **Social security number (personnummer)** stored on a custom Google Workspace user object. The schema and attribute name holding the value is also required.
* **Outgoing TCP/443 communication.** To be able to communicate with BankID backend and Google Workspace services.

## Configuration

### Download and add configuration

Remember that this use case does not describe installation of the products. Products are expected to be installed in advance.

1. Download ZIP containing configuration for Web
   1. Click [USE\_CASE\_LINK](https://share.fortifiedid.se/index.php/s/goRGFJiynFMZqC5/download) to download customer folder for Web.
2. Add **Integrity WEB** configuration to your environment.
   1. Rename the existing customer folder to customer\_ORG.\
      Add the **customer** folder to your \\..\fortifiedid\web\ folder.

### Update the Google Workspace configuration

Setup the Google Workspace configuration [using this guide.](https://docs.fortifiedid.se/use-cases/misc/google/common-configuration-for-google-workspace)

### Update the configuration to map your environment

The downloaded folders contains all information needed. For example, a test certificate and metadata files are included.\
However, some data needs to be changed to map your environment. Also the http ports might need to be changed if they are not available in your environment.

#### Integrity Web

In this section we will look at parts of the configuration and add/replace data for your environment. In this use case we are using the globals concept which is using variables to easily replace data specific to an environment or if a value is used in many places just update it in one place.

* Open the file customer/config/globals.json. Change according to the instructions below.

1. **base\_dir**

   1. base\_dir is the top folder where data is located that you do not want to be overwritten by an upgrade. Update the **base\_dir** folder to map your installation.
      1. For Windows the value should be:\
         "base\_dir": "../customer"
      2. For Docker, the value should be:\
         "base\_dir": ".",

   ```json
   "base_dir": "../customer"
   ```
2. **host**\
   Set the host value to your Integrity Web DNS name entry, including https\://.

   ```json
   "host": "https://be4f-194-68-171-97.ngrok-free.app",
   ```
3. **http**

   Update the http information to map your environment. This is the port that Integrity Web will use to host the SAML IdP service.\
   ! The recommendation is to always use SSL to encrypt the communication to Integrity Web.<br>

   ```json
    "http": {
               "http_port": 443,
               "http_use_ssl": true
           }
   ```
4. **keystore - https**

   Either you use the test certificate provided by us, if so you do not need to change anything. If you have a keystore then update the values below to point to your keystore.

   1. Find in section: **keystore**

      <pre class="language-json"><code class="lang-json"><strong>"https": {
      </strong>                "ref": {
                          "path": "${globals.base_dir}/config/resources_internal/certificates/fortifiedid.p12",
                          "password": "password"
                      },
                      "http_key_alias": "1",
                      "http_key_password": "password"
                  },
      </code></pre>
5. **keystore - bankid**

   For connecting against BankID test environment, you don't need to do anything.\
   For production connectivity, please use your BankID keystore and change the variables below to reflect that. Truststore changes will not be needed.\
   Find in section: **keystore**<br>

   ```json
              "bid": {
                   "ssl_keystore_path": "${globals.base_dir}/config/resources_internal/certificates/BankID/bankid_test_rp.p12",
                   "ssl_keystore_password": "qwerty123",
                   "ssl_key_alias": "1",
                   "ssl_key_password": "qwerty123",
                   "ssl_truststore_path": "${globals.base_dir}/config//resources_internal/certificates/trust_jks/bankidtrust.jks",
                   "ssl_truststore_password": "password"
               },
   ```
6. **keystore - saml**\
   The keystore used to sign SAML assertions.\
   For test environments, you may use the test certificate provided by us, if so you do not need to change anything.\
   For production environments, you should use your own keystore and update the values below to point to that keystore.<br>

   ```json
               "saml": {
           	    "alias": "1",
            	    "key_password": "password",
            	    "password": "password",
          		    "path": "${globals.base_dir}/config/resources_internal/certificates/fortifiedid.p12"
               }
   ```

Save the globals.json file.

* Open customer/config/resources\_internal/saml\_sp\_metadata\_files/sp\_google.xml

1. Replace *YOUR\_DOMAIN* with your google domain. (Example: fortifiedid.se)
2. Save the file

* Start the Integrity web service and verify the start by looking through the server.log file.

### Setup Google to use third party IdP

#### Prepare values

* Open a web browser and browse to https\://\<integrity\_web\_host>/saml/metadata/integrity\_idp\_google
* Open the downloaded file in a text editor
* Copy these values:
  * *SingleSignOnService->Location*. (Sign-in page URL)
  * *SingleLogoutService->Location*. (Sign-out page URL)
* Extract the certificate (public) from the keystore used for IdP token signing:
  * Get the keystore file pointed to in globals.json (keystore->saml->path)
  * Extract the certificate to a file using any tool of your choice (openSSL, keystore explorer, windows cert.mmc, keytool)

#### Setup Google

* Follow [these instructions](https://support.google.com/a/answer/12032922?hl=en\&fl=1\&sjid=8258206362093893712-NA) -> *Configure the SSO profile for your organization.*
  * Add the Sign-in page URL fetched in previous step
  * Add the Sign-out page URL fetched in previous step
  * Upload the certificate fetched in previous step
  * Select to use a domain-specific issuer
  * Skip Change password URL
* ([This page ](https://apps.google.com/supportwidget/articlehome?hl=en\&article_url=https%3A%2F%2Fsupport.google.com%2Fa%2Fanswer%2F12032922%3Fhl%3Den\&assistant_id=generic-unu\&product_context=12032922\&product_name=UnuFlow\&trigger_context=a\&fragment=service_URLs\&fragment=service_URLs)instructs you how to apply the third party IdP to a subset of users or all users, depending on your setup and requirements).

## Testing the use case

1. Browse to <https://mail.google.com> and enter your email address, or browse to the domain-specific url <https://mail.google.com/a/\\>\<your\_domain>.
2. You should be redirected to the IdP
3. Authenticate with BankID
4. You should be redirected back to Google with a SAML assertion.
5. You should now be logged in to Google.

Check server.log file to find errors. Fix accordingly.

## **Complete config.json file** of Integrity Web

The config.json of Web can be found in the associated zip-file in this use case.

{% code lineNumbers="true" %}

```
{
    "globals": "@include:globals.json",
    "modules": [
        {
            "name": "SAML",
            "config": {
                "http_port": "${globals.http.port}",
                "http_use_ssl": true,
                "http_keystore_ref": {
                    "path": "${globals.keystore.https.ref.path}",
                    "password": "${globals.keystore.https.ref.password}"
                },
                "http_key_alias": "${globals.keystore.https.http_key_alias}",
                "http_key_password": "${globals.keystore.https.http_key_password}",
                "enable_http": true,
                "metadata_cache": "${globals.base_dir}/config/resources_internal/saml_cache/",
                "metadata_template": [
                    {
                        "id": "integrity_idp_google",
                        "metadata_file_path": "${globals.base_dir}/config/resources_internal/saml_templates/integrity_google_idp_template.xml",
                        "sign_ref": [
                            {
                                "keystore": {
                                    "path": "${globals.keystore.saml.ssl_keystore_path}",
                                    "password": "${globals.keystore.saml.ssl_keystore_password}",
                                    "alias": "${globals.keystore.saml.ssl_key_alias}",
                                    "key_password": "${globals.keystore.saml.ssl_key_password}"
                                }
                            }
                        ]
                    }
                ],
                "metadata": [
                    {
                        "path": "${globals.base_dir}/config/resources_internal/saml_sp_metadata_files/sp_google.xml"
                    }
                ]
            }
        },
        {
            "name": "CefEventModule",
            "config": {}
        },
        {
            "name": "HttpClient",
            "config": {
                "name": "default",
                "idle_timeout_ms": 5000,
                "connect_timeout_ms": 5000,
                "ssl_trust_all": true
            }
        },
        {
            "name": "HttpClient",
            "config": {
                "name": "bankid_http_outgoing",
                "idle_timeout_ms": 5000,
                "connect_timeout_ms": 5000,
                "ssl_keystore_path": "${globals.keystore.bid.ssl_keystore_path}",
                "ssl_keystore_password": "${globals.keystore.bid.ssl_keystore_password}",
                "ssl_key_alias": "${globals.keystore.bid.ssl_key_alias}",
                "ssl_key_password": "${globals.keystore.bid.ssl_key_password}",
                "ssl_truststore_path": "${globals.keystore.bid.ssl_truststore_path}",
                "ssl_truststore_password": "${globals.keystore.bid.ssl_truststore_password}",
                "request_timeout_ms": 3500
            }
        },
        {
            "name": "AuthN",
            "enabled": true,
            "config": {
                "webroot_dir": "web",
                "http_port": "${globals.http.port}",
                "http_use_ssl": true,
                "http_keystore_ref": {
                    "path": "${globals.keystore.https.ref.path}",
                    "password": "${globals.keystore.https.ref.password}"
                },
                "http_key_alias": "${globals.keystore.https.http_key_alias}",
                "http_key_password": "${globals.keystore.https.http_key_password}",
                "authenticators": [
                    {
                        "id": "auth00",
                        "type": "SAMLIDP",
                        "config": {
                            "force_re_auth": true,
                            "context_path": "/saml/authn/idp",
                            "base_path": "/saml/authn",
                            "idp": "${globals.host}/idp",
                            "chain": [
                                {
                                    "id": "bidomd",
                                    "required": true
                                },
                                {
                                    "id": "google_lookup",
                                    "required": true
                                }
                            ],
                            "assertion_config": [
                                {
                                    "target_sp": [
                                        "*"
                                    ],
                                    "nameid_parameter": "primaryEmail"
                                }
                            ]
                        }
                    },
                    {
                        "id": "bidomd",
                        "type": "BidOnMobileDevice",
                        "config": {
                            "mode": "test",
                            "base_path": "/saml/authn",
                            "internal_http_destination": "bankid_http_outgoing",
                            "webroot_dir": "web/authenticator/bankid/omd"
                        }
                    },
                    {
                        "id": "google_lookup",
                        "type": "AuthController",
                        "config": {
                            "base_path": "/saml/authn",
                            "pipe_id": "pipe_google_lookup",
                            "fail_location": "/error",
                            "failed_redirect": [
                                {
                                    "pattern": ".*404 Not Found.*",
                                    "target": "/notfound"
                                }
                            ]
                        }
                    }
                ]
            }
        },
        {
            "name": "Pipes",
            "config": {
                "pipes": [
                    {
                        "id": "pipe_google_lookup",
                        "config": {
                            "valves": [
                                {
                                    "name": "CreateJwt",
                                    "config": {
                                        "jwt_ttl": 5,
                                        "jwt_claims": {
                                            "iss": "${globals.google.serviceaccount}",
                                            "scope": "https://www.googleapis.com/auth/admin.directory.user.readonly",
                                            "aud": "https://oauth2.googleapis.com/token"
                                        },
                                        "keystore": {
                                            "path": "${globals.keystore.google.ssl_keystore_path}",
                                            "password": "${globals.keystore.google.ssl_keystore_password}",
                                            "type": "PKCS12"
                                        },
                                        "keystore_password": "${globals.keystore.google.ssl_key_password}",
                                        "keystore_alias": "${globals.keystore.google.ssl_key_alias}"
                                    }
                                },
                                {
                                    "name": "HttpPost",
                                    "config": {
                                        "url": "https://oauth2.googleapis.com/token",
                                        "destination": "default",
                                        "parameters": {
                                            "assertion": "{{{item.jwt}}}",
                                            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer"
                                        },
                                        "content_type": "application/x-www-form-urlencoded"
                                    }
                                },
                                {
                                    "name": "HttpGet",
                                    "config": {
                                        "url": "https://admin.googleapis.com/admin/directory/v1/users/?domain=${globals.google.domain}&query=${globals.google.custom_attribute}%3D{{{request.personalNumber}}}",
                                        "destination": "default",
                                        "headers": {
                                            "Authorization": "Bearer {{{state.body.access_token}}}"
                                        },
                                        "content_type": "application/json"
                                    }
                                },
                                {
                                    "name": "ExportsPut",
                                    "config": {
                                        "name": "primaryEmail",
                                        "value": "{{{state.body.users.0.value.primaryEmail}}}"
                                    }
                                }
                            ]
                        }
                    }
                ]
            }
        }
    ]
}
```

{% endcode %}

<br>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.fortifiedid.se/use-cases/misc/google/protect-google-workspace-with-eid-mfa.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
