Skip to content

Latest commit

 

History

History
216 lines (168 loc) · 8.79 KB

File metadata and controls

216 lines (168 loc) · 8.79 KB

SF1601: Afsend post

https://digitaliseringskataloget.dk/integration/sf1601

MeMo version

If the MeMo version is not set explicitly on a message, this library set the version to MeMo 1.2.

The default version can be overridden by settings the SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION environment variable to one of the allowed values 1.1 or 1.2, e.g.

SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION=1.1

Calling this service is extremely simple, but it's extremely hard to navigate the documentation and understand how to actually authenticate and call the service.

As mentioned in SF1601 Programmer’s Guide, section 1.2 (Compatible frameworks) (cf. https://digitaliseringskataloget.dk/integration/sf1601), for calling the web service

The usage pattern is as follows:

(1) Request a SAML-token from the Security Token Service (REST) (2) Exchange the SAML-token to an Access Token (REST) (3) Invoke the service with the Access Token (REST)

All three step are simple basic HTTP-requests and thus compatible with all frameworks.

In the following section we will reveal what the first two simple HTTP-requests (sic!) actually are.

The Programmer’s Guide is easily accessible and can be opened using this simple command:

(cd /tmp; curl --location https://docs.kombit.dk/integration/sf1601/1.0/pakke --output SF1601.zip; unzip SF1601.zip 'SF1601 Bilag*.zip'; unzip "SF1601 Bilag*.zip"; open "SF1601 Bilag*/SF1601 Programmer's Guide.pdf")

Viewing the documentation as Swagger/OpenAPI

When downloading the documentation from https://digitaliseringskataloget.dk/integration/sf1601 you get a zip-file. This zip-file contains another zip file named "SF1601 Teknisk bilag.zip". This zip-file contains multiple json-files that can be viewed in a Swagger/OpenAPI viewer. One such viewer can be found here: https://editor.swagger.io/.

Calling the web service

Assuming you have your certificate in the file certificate.p12 (or certificate.pfx) you first have to extract the public certificate and the private key (cf. section 1.6 Certifikater in https://digitaliseringskataloget.dk/files/integration-files/300620221422/Adgangsstyring%20for%20Brugere%20-%20Egen%20Identity%20Provider.pdf):

openssl pkcs12 -in certificate.p12 -out certificate.crt -nokeys -password pass:'«the p12 password»'
openssl pkcs12 -in certificate.p12 -out certificate.priv.key -nodes -nocerts -password pass:'«the p12 password»'

Both the public certificate (certificate.crt) and the private key (certificate.priv.key) are in the PEM format.

We also need the public certificate on a single line:

openssl x509 -in certificate.crt -trustout | sed '1,1d;$d' | tr -d '\n'

Now we're ready to get to work.

1. Getting a SAML token

A SAML token can be issued by POST'ing to https://adgangsstyring.eksterntest-stoettesystemerne.dk/runtime/api/rest/wstrust/v1/issue (for test) or https://adgangsstyring.stoettesystemerne.dk/runtime/api/rest/wstrust/v1/issue (for production) with the public certificate and the private key. The request payload must be a JSON object containing your CVR number, your one-line public certificate and an entity id depending on which service method you want to call (cf. https://digitaliseringskataloget.dk/integration/sf1601):

{
    "TokenType": "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0",
    "RequestType": "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue",
    "KeyType": "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey",
    "AnvenderKontekst": {
        "Cvr": «your CVR number»
    },
    "UseKey": «one-line public certificate»,
    "AppliesTo": {
        "EndpointReference": {
            "Address": «the entity id»
        }
    },
    "OnBehalfOf": null
}

Apparently "UseKey" is not documented anywhere, but is required. The value must be the public certificate on a single line (without -----(BEGIN|END) CERTIFICATE-----).

Using curl the SAML token request can be made like this:

# Export some variables for use in the JSON payload.
export CVR=«your CVR number»
# We want to call “KombiPostAfsend” (cf. https://digitaliseringskataloget.dk/integration/sf1601)
export ENTITY_ID=http://entityid.kombit.dk/service/kombipostafsend/1
export USE_KEY=$(openssl x509 -in certificate.crt -trustout | sed '1,1d;$d' | tr -d '\n')
curl --silent --cert certificate.crt --key certificate.priv.key https://adgangsstyring.eksterntest-stoettesystemerne.dk/runtime/api/rest/wstrust/v1/issue --request POST --header 'content-type: application/json' --data @- <<JSON
{
    "TokenType": "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0",
    "RequestType": "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue",
    "KeyType": "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey",
    "AnvenderKontekst": {
        "Cvr": "$CVR"
    },
    "UseKey": "$USE_KEY",
    "AppliesTo": {
        "EndpointReference": {
            "Address": "$ENTITY_ID"
        }
    },
    "OnBehalfOf": null
}
JSON

Using jq you can put the SAML token into a variable, SAML_TOKEN, for easy use in the next step:

export ENTITY_ID=http://entityid.kombit.dk/service/kombipostafsend/1
export CVR=«your CVR number»
export USEKEY=$(cat certificate.usekey)
export SAML_TOKEN=$(curl --silent --cert certificate.crt --key certificate.priv.key https://adgangsstyring.eksterntest-stoettesystemerne.dk/runtime/api/rest/wstrust/v1/issue --request POST --header 'content-type: application/json' --data @- <<JSON | jq --raw-output '.RequestedSecurityToken.Assertion'
{
    "TokenType": "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0",
    "RequestType": "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue",
    "KeyType": "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey",
    "AnvenderKontekst": {
        "Cvr": "$CVR"
    },
    "UseKey": "$USEKEY",
    "AppliesTo": {
        "EndpointReference": {
            "Address": "$ENTITY_ID"
        }
    },
    "OnBehalfOf": null
}
JSON
)

2. Getting an access token

The SAML token can be exchanged for an access token by POST'ing it to https://exttest.serviceplatformen.dk/service/AccessTokenService_1/token (test) or https://prod.serviceplatformen.dk/service/AccessTokenService_1/token (production).

curl --silent --cert certificate.crt --key certificate.priv.key https://exttest.serviceplatformen.dk/service/AccessTokenService_1/token --data-urlencode "saml-token=$SAML_TOKEN"

When succesful, the response will look like

{
  "access_token": "1889e404-eada-4046-9283-73bda5a44e07",
  "token_type": "Holder-of-key",
  "expires_in": 3600
}

And we're now ready for the final step.

3. Calling the web service method

Using the access token, we can now call the KombiPostAfsend method using the token in the authorization header:

curl --silent --cert certificate.crt --key certificate.priv.key https://exttest.serviceplatformen.dk/service/KombiPostAfsend_1/kombi --header 'authorization: Holder-of-key 1889e404-eada-4046-9283-73bda5a44e07' --data @- <<'XML'
<kombi_request><KombiValgKode/></kombi_request>
XML

Generating classes from XML Schema Definitions

We use xsd2php to generate PHP classes from XML Schema Definitions (XSD) from https://www.digitaliser.dk/resource/5248921 (MeMo_XSD.zip).

See bin/xsd2php for details on how XSDs are converted to PHP classes.

To generate or update the generated PHP classes, run

docker run --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest composer install
docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/xsd2php

Sending test messages

A console command, bin/SF1601/kombipostafsend, can be used to send a test message using the “KombiPostAfsend” service:

docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/SF1601/kombipostafsend –-help

To test with another default MeMo version, e.g. 1.1, run

docker run --env SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION=1.1 --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/SF1601/kombipostafsend