> For the complete documentation index, see [llms.txt](https://docs.fortifiedid.se/solutions/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/solutions/verifiable-credentials/add-integrity-web-as-an-oidc-attribute-provider-for-microsoft-entra-azure-verifiable-credentials.md).

# Add Integrity Web as an OIDC attribute provider for Microsoft Entra (Azure) Verifiable Credentials

### &#x20;<a href="#prerequisites-0" id="prerequisites-0"></a>

### Purpose <a href="#prerequisites-0" id="prerequisites-0"></a>

Microsoft Entra Verified ID is a verifiable credential issuance and verification service provided by Microsoft Azure. With MS Entra users are able to generate and present digital credentials. These credentials can be also be verified by applications, using MS Entra Verified ID as the platform.

Microsoft Entra Verified ID supports configuring an external OpenID Connect Provider, such as Fortified ID Integrity Web, as an attribute (claim) provider for verifiable credentials. The OpenID Connect Provider asserts and id\_token with a set of attributes (claims) which is used to generate the verifiable credential.

The purpose of this document is to explain how to set this up with Fortified Integrity Web and Microsoft Entra ID. It also includes instruction on how to test it, using a sample app from Microsoft.

The document describes a use case where the OpenID Connect Provider (Integrity Web) uses Freja OrganisationID as the authentication method. The organisation identifier (userID) is returned as the preferred\_username claim which is then used to populate the Verifiable Credential.

\
**Please note** that this is only one use case example. Integrity Web can be configured to use a vast range of authentication methods and data sources for claim population, based on the Verifiable Credential requirement. Please contact Fortified ID for further assistance.

### Prerequisites

* [Azure prerequisites](https://learn.microsoft.com/en-us/azure/active-directory/verifiable-credentials/verifiable-credentials-configure-tenant#prerequisites) in place
* Integrity web installed
* Front end web server, such as Apache or Nginx, installed on the same machine as Integrity
* Front end web server must be exposed to the internet with a resolvable DNS name and valid https certificate (you can use *ngrok* to temporary expose local machines to the internet for testing purposes etc)
* Front end web server should proxy all http traffic to the Integrity web server, with one exception:\
  \
  \&#xNAN;*ProxyPass /.well-known !*\
  *ProxyPassReverse /.well-known !*

### Configure your Azure tenant for Verifiable Credentials <a href="#procedure-1" id="procedure-1"></a>

#### Create key vault

Follow these instructions to create a key vault. Do not proceed with setting access policies, we will come to that later.

* [Create key vault](https://learn.microsoft.com/en-us/azure/active-directory/verifiable-credentials/verifiable-credentials-configure-tenant#create-a-key-vault)\
  \
  Example:

#### Enable Verified ID

Set up Verified ID, using [this instruction.](https://learn.microsoft.com/en-us/azure/active-directory/verifiable-credentials/verifiable-credentials-configure-tenant#set-up-verified-id)

Example:<br>

#### Set access policies

Set access policies for the Verified ID service principals, using [this instruction.](https://learn.microsoft.com/en-us/azure/active-directory/verifiable-credentials/verifiable-credentials-configure-tenant#set-access-policies-for-the-verified-id-service-principals)

Example:<br>

#### Register the sample app

The sample test app must be authorized to use Entra for issuance of Verifiable Credentials.

Set it up in Azure, using [this instruction](https://learn.microsoft.com/en-us/azure/active-directory/verifiable-credentials/verifiable-credentials-configure-tenant#register-an-application-in-azure-ad). Follow [this instruction](https://learn.microsoft.com/en-us/azure/active-directory/verifiable-credentials/verifiable-credentials-configure-tenant#grant-permissions-to-get-access-tokens) to grant access.

Example:

Click Certificates and secrets

Create a new Client Secret

Set a description and an expiry date

Make sure to note the client secret value as this will be used in a later step.

Example:

### Setup Fortified ID Integrity Web as an OpenID Connect Provider

In this step, Fortified ID Integrity Web will be configured as an OpenID Connect Provider. The supporting instruction for this setup can be found [here](https://docs.fortifiedid.se/fortified-integrity/authenticators/protocol-managers/oidc/authorization-code-flow).

The complete server-config.json can be found here. The annotated values should be changed to match your environment. Please click on the annotation for instructions.

<pre><code>{
  "globals": {
    "base_dir": ".",
    "keystore": {
      "alias": "1",
      "key_password": "<a data-footnote-ref href="#user-content-fn-1">XXXXXX</a>",
      "password": "<a data-footnote-ref href="#user-content-fn-2">XXXXXX</a>",
      "path": "<a data-footnote-ref href="#user-content-fn-3">/opt/fortified/integrity/config/my.p12</a>"
    },
    "oidc_op_address": "<a data-footnote-ref href="#user-content-fn-4">https://9475-85-230-84-162.ngrok-free.app</a>",
    "relyingpartys": [
      {
        "client_id": "azureapp",
        "client_secret": "<a data-footnote-ref href="#user-content-fn-5">XXXXXX</a>",
        "redirect_uri": [
          "vcclient://openid/"
        ]
      }
    ]
  },
  "modules": [
    {
      "name": "CefEventModule",
      "config": {}
    },
    {
      "name": "HttpClient",
      "config": {
        "name": "default",
        "idle_timeout_ms": 5000,
        "connect_timeout_ms": 5000
      }
    },
    {
      "name": "HttpClient",
      "config": {
        "name": "freja",
        "idle_timeout_ms": 5000,
        "connect_timeout_ms": 5000,
        "ssl_keystore_path": "<a data-footnote-ref href="#user-content-fn-6">/opt/fortified/integrity/config/freja.p12</a>",
        "ssl_keystore_password": "<a data-footnote-ref href="#user-content-fn-7">XXXXXX</a>",
        "ssl_key_alias": "1",
        "ssl_key_password": "<a data-footnote-ref href="#user-content-fn-8">XXXXXX</a>",
        "ssl_truststore_path": "<a data-footnote-ref href="#user-content-fn-9">/opt/fortified/integrity/config/frejatrust.jks</a>",
        "ssl_truststore_password": "<a data-footnote-ref href="#user-content-fn-10">XXXXXX</a>",
        "request_timeout_ms": 3500
      }
    },
    {
      "name": "OIDCModule",
      "enabled": true,
      "config": {
        "providers": [
          {
            "http_context": "/oidc",
            "http_port": <a data-footnote-ref href="#user-content-fn-11">8081</a>,
            "discovery_meta": {
              "issuer": "${globals.oidc_op_address}/oidc/tenant1",
              "authorization_endpoint": "${globals.oidc_op_address}/test/authn/oidc",
              "token_endpoint": "${globals.oidc_op_address}/oidc/tenant1/token-endpoint",
              "userinfo_endpoint": "${globals.oidc_op_address}/oidc/tenant1/userinfo-endpoint",
              "jwks_uri": "${globals.oidc_op_address}/oidc/tenant1/.well-known/openid-configuration/jwks",
              "end_session_endpoint": "${globals.oidc_op_address}/test/authn/oidc/logout",
              "scopes_supported": [
                "openid"
              ],
              "response_types_supported": [
                "code"
              ],
              "grant_types_supported": [
                "authorization_code"
              ],
              "subject_types_supported": [
                "public"
              ],
              "id_token_signing_alg_values_supported": [
                "RS256"
              ],
              "token_endpoint_auth_methods_supported": [
                "client_secret_post",
                "client_secret_basic"
              ],
              "claims_supported": [
                "iss",
                "ver",
                "sub",
                "preferred_username"
              ],
              "request_parameter_supported": true
            },
            "tenant_op_path": "/tenant1",
            "keystore": "${globals.keystore}",
            "sign_jwt_keystore_password": "${globals.keystore.password}",
            "sign_jwt_keystore_alias": "${globals.keystore.alias}",
            "rps": "${globals.relyingpartys}"
          }
        ]
      }
    },
    {
      "name": "AuthN",
      "enabled": true,
      "config": {
        "context_path": "/test/authn",
        "webroot_dir": "web",
        "http_port": <a data-footnote-ref href="#user-content-fn-11">8081</a>,
        "authenticators": [
          {
            "id": "oidc",
            "type": "OIDCAuthCodeFlow",
            "config": {
              "base_path": "/test/authn",
              "context_path": "/test/authn/oidc",
              "failure_location": "/authn/failure.html",
              "simple_logout": true,
              "required_request_parameters": [
                "response_type",
                "client_id",
                "redirect_uri",
                "scope"
              ],
              "required_authenticators": [
                "freja01"
              ],
              "jwt_headers": {
                "kid": ""
              },
              "jwt_claims": {
                "aud": "azureapp",
                "sub": "{{{request.organisationIdIdentifier}}}",
                "nonce": "{{{request.nonce}}}",
                "iss": "${globals.oidc_op_address}/oidc/tenant1",
                "preferred_username": "{{{request.organisationIdIdentifier}}}"
              },
              "userinfo_claims": {},
              "token_endpoint_ttl": 90000,
              "userinfo_endpoint_ttl": 90000,
              "keystore": "${globals.keystore}",
              "sign_jwt_keystore_password": "${globals.keystore.password}",
              "sign_jwt_keystore_alias": "${globals.keystore.alias}",
              "rps": "${globals.relyingpartys}"
            }
          },
          {
            "id": "freja01",
            "type": "FrejaWithQRCode",
            "config": {
              "base_path": "/test/authn",
              "internal_http_destination": "freja",
              "webroot_dir": "web/authenticator/freja/freja_qr_or_appswitch",
              "attributesToReturn": [
                "ORGANISATION_ID_IDENTIFIER"
              ],
              "minRegistrationLevel": "EXTENDED",
              "mode": "prod_org"
            }
          }
        ]
      }
    }
  ]
}
</code></pre>

Restart integrity web

Verify the initial configuration by browsing to https\://\<your\_external\_dns\_name>/oidc/tenant1/.well-known/openid-configuration.\
OIDC discovery json should now be displayed.

Check log files for errors if no discovery json is presented. Fix the error(s) and try again.

### Add Credential Provider to Azure

Open the Azure portal

Select Verified ID

Credentials

Click Add Credential

Select Custom Credential

Enter a name (in this config example we used *FortifiedIDEmployeeType)*

Add this json configuration below **Display**. Please change to suite your environment.

```
{
"locale": "en-US",
"card": {
    "backgroundColor": "#FFFFFF",
    "description": "Use your verified credential to prove that you are an employee of Fortified ID.",
    "issuedBy": "Fortified ID",
    "textColor": "#000000",
    "title": "Fortified ID Employed",
    "logo": {
    "description": "Fortified ID logo",
    "uri": "https://fortifiedid.se/images/outline_fortified.png"
    }
},
"consent": {
    "instructions": "Sign in with your Fortified ID OpenID Connect Provider to get your card.",
    "title": "Sign in with your Fortified ID OpenID Connect Provider to get your card."
},
"claims": [
    {
    "claim": "vc.credentialSubject.userName",
    "label": "Fortified ID userID",
    "type": "String"
    }
]
}
```

Add this json configuration below **Rules**. Please change to suite your environment.

```
{
 "attestations": {
     "idTokens": [
     {
         "clientId": "azureapp",
         "configuration": "https://<your_external_dns_name>/oidc/tenant1/.well-known/openid-configuration",
         "redirectUri": "vcclient://openid/",
         "scope": "openid profile",
         "mapping": [
         {
             "outputClaim": "userName",
             "required": true,
             "inputClaim": "$.username",
             "indexed": false
         }
         ],
         "required": false
     }
     ]
 },
 "validityInterval": 2592000,
 "vc": {
     "type": [
     "FortifiedIDEmployeeType"
     ]
 }
 }
```

Click Create

Once created, open the Verifiable Credential

Click Issue Credential

Take note of the *Authority* and *Manifest* values. These will be used later in the instruction.

### Configure the test application

* Download the test application from <https://github.com/Azure-Samples/active-directory-verifiable-credentials-node>
* Unzip in any location
* Open 1-node-api-idtokenhint/config.json file in a text editor
* Set these configuration parameters:

**azTenantId** - Your Azure tenant ID

**azClientId** - The client ID for the app (value from the process Register the app in Azure)

**azClientSecret** - The client secret for the app (value from the process Register the app in Azure)

**CredentialManifest -** The manifest URL of the credential. Recevied in previous step.

**IssuerAuthority -** The Authority. Recevied in previous step.

**VerifierAuthority -** The Authority. In this sample, this is the same as IssuerAuthority. Recevied in previous step.

**Full example:**

```
{
    "azTenantId": "bf70b0ec-2b39-xxxx-yyyy-zzzzzzzzzzzz",
    "azClientId": "ad980489-0c1e-xxxx-yyyy-zzzzzzzzzzzz",
    "azClientSecret": "BIY8Q~zGoxyz123456abc",
    "azCertificateName":  "",
    "azCertThumbprint":  "",
    "azCertificatePrivateKeyLocation":  "",
    "CredentialManifest": "https://verifiedid.did.msidentity.com/v1.0/tenants/bf70b0ec-2b39-xxxx-yyyy-zzzzzzzzzzzz/verifiableCredentials/contracts/5ea12331-d92d-89a6-915c-zzzzzzzzzzzz/manifest",
    "IssuerAuthority": "did:web:9475-85-230-84-162.ngrok-free.app",
    "VerifierAuthority": "did:web:9475-85-230-84-162.ngrok-free.app"
}
```

* Save the file.
* Open 1-node-api-idtokenhint/presentation\_request\_config.json file in a text editor
* Change these configuration parameters:

**acceptedIssuers** - set to Issuer DID

**type -** set to Verifiable Credentials type

Example:

```
.
.
"requestedCredentials": [
    {
      "type": "FortifiedIDEmployeeType",
      "acceptedIssuers": [ "did:web:9475-85-230-84-162.ngrok-free.app" ]
    }
  ],
  .
  .
```

Save the file

#### Try it out

* Open a terminal
* Change folder to the directory
* Run the command *npm install && npm start*
* The application runs on <http://localhost:8080>. However, it's not able to test the Verifiable Credentials Flow from a local application.\
  ! To test the Verifiable Credentials flow, the application must be reachable from the internet, on a public DNS. For testing purposes, ngrok may be used to expose the application to the internet.
* Browse to the application (using the external DNS). This page should be presented:

Click GET CREDENTIAL to test issuance

Click VERIFY CREDENTIAL to test presentation / verification

In case of errors, please view the console log of the application to check for configuration issues.

In case of errors on the OpenID Connect Provider during issuance, please view the Fortified ID Integrity log files.

[^1]: Enter password to your keystore private key.

[^2]: Enter password to your keystore.

[^3]: Create (if not created before) a p12 keystore. This keystore will be used for signing tokens. Place it on disk and refer the file from here.

[^4]: Change to your external DNS name (must be accessible from the internet).

[^5]: Enter any value for a client\_secret. Take a note of this value as it will be used in later step.

[^6]: Point to your Freja keystore (the keystore required to communicate with Freja backend)

[^7]: Set Freja keystore password

[^8]: Set Freja keystore private key password

[^9]: Point to the Freja truststore (containing CAs used to protect the Freja backend)

[^10]: Enter the Freja truststore password

[^11]: Enter the Integrity web http listening port
