-
Notifications
You must be signed in to change notification settings - Fork 6
Client
This example client application helps developers understand and debug:
- authentication using a client id and a client secret (for non-production environements)
- authentication using a signed JWT
- invoking an example service (forthcoming)
This example client uses .NET 6.0, which you can use to create server apps, command line apps, or WinForms desktop apps.
Build the project by running dotnet build in the command line from Client/src/.
Fill out the fields in Client/src/appsettings.json with the appropriate configuration values. Run the project by running dotnet run in the command line from Client/src/.
This example client application follows several steps to authenticate and authorize the user before the request is processed by the APIs. After the client is authenticated, it submits a single request given in the configuration file.
The configuration file appsettings.json that referred to in the following instructions is located at Client/src/appsettings.json.
-
First, the program reads the configuration values from
appsettings.json. Then ifUseKeyFileistrue, it will attempt to read the key from the supplied .pfx file (see below for instructions to create this file). IfUseKeyFileisfalse, the program will instead useClientIdandClientSecretto authenticate itself. -
Next, the program will determine what URL endpoint it should get the access token from. It gets this URL by querying the given
Authoritystring suffixed with/.well-known/openid-configuration. If you are getting 404 Not Found errors at this point, make sure that you've entered only the Keycloak base URL in theAuthorityfield ofappsettings.json. The result of this operation is another URL that the client application will use for the actual authentication. -
The program sends a request to the URL it received in step 2. In this request it will include the
AudienceandScopeparameters fromappsettings.json, as well as the chosen authentication credentials (key file or ID and secret). If the authentication is successful, the server will return a JSON Web Token that will provide the client with access to the stated scope.
With the access token the client receives in step 3 of Authentication, the client is ready to submit a request to the ServiceUrl given in the same appsettings.json file. This should be the URL to one of the nine API services within this application. The API must be active and ready to receive requests.
-
The client loads the HL7-v2 formatted data payload given in
appsettings.jsoninto the program. It encodes it in base64 and wraps it in a FHIR-compliant JSON object. This JSON object will form the body of the request to the API. -
The client creates an HTTPRequestMethod object and populates it with the appropriate content and headers. The headers are
Content-Type: application/jsonandAuthorization: Bearer {access token}. The content is the FHIR-style JSON object from step 2, converted to a string. -
The HTTP request is sent to the destination service which verifies the access token. If the token is not successfully verified and authorized, a 401 Unauthorized error response will be returned to the client. Otherwise, the response will reflect the success or failure of the operation. If you get errors at this stage during testing, check the logs of the API (if you have access to them) to see more detailed messages. Also check the logs of the PharmaNet environment the API is using to see if it recorded an interaction.
Below is an example appsettings.json file. In this example, no key file is provided so the program will be configured to use the client ID and secret. This example assumes that you have the Claim API running in a Docker container mapped to localhost:8181. The HL7 Payload is one of the sample messages from the k6 test suite for the Claim service.
{
"UseKeyFile": "false",
"ServiceUrl": "http://localhost:8181/api/v1/Claim",
"Payload": "MSH|^~&|PHARMACY|VENDOR_BC01000971|PNP|PP||RB:69.11.119.122|ZPN|180736|D|2.1||\rZZZ|TDT||180736|P1|XXBSK||||\rZCA|000001|03|30|KC|16|\rZCB|BC000001ED|220823|180736\rZCC|||||||||||\rZCF|220823|000000000|999999999\r",
"OpenIdConnect": {
"Audience": "",
"Authority": "https://common-logon-test.hlth.gov.bc.ca/auth/realms/moh_applications",
"ClientId": "{your-client-id}",
"ClientSecret": "{your-client-secret}",
"Scope": "openid system/Claim.write system/Claim.read"
},
"JwtSigning": {
"CertificatePassword": "{your-certificate-password}",
"CertificatePfxFile": "{your-certificate.pfx}"
}
}Keycloak can generate a keystore.jks file containing the private key for the client application. It can also accept the upload of a JKS or PFX certificate or PEM for validating the signed JWT provided by the client during authentication. You can also have Keycloak point to a URL where you keep your public keys in a JWKS file. Then you are responsible for generating/maintaining your certificates and their corresponding public/private key pairs. This can help to minimize configurations in Keycloak.
In this example client, the Keycloak administrator provides us with a keystore.jks file and its password. We need to convert this file to a pfx certificate file to allow us to sign the JWT.
Here are the steps, assuming that the private key is created by the Keycloak administrator for you.
If Keycloak is supplying your client's private key, it will hang onto the public key to verify your client authentication. Keycloak provides a JKS file format for your private keys. It will need to be converted to a pfx certificate file in order to use it with the Client code provided here.
You will need to know the JKS source password, but first you will be prompted to enter a destination password for the pfx file. It will need to be at least 6 characters long.
keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.pfx -srcstoretype jks -deststoretype pkcs12You can now use this pfx file in the Client example, and its private key will be used to sign the JWT for OAuth 2.0 authentication against Keycloak. You will use the password you set in the appsettings.json configuration file as well.
You will want to research ways to store certificates securely and in a way that makes them available to the app configurations.
If you want to check the signing of the JWT using your public key, you can create a public key file and then use it to check the signing. In this case, follow these steps.
Using openssl, you will now convert the pkcs12 file to pem format. The pem will also require a passphrase to be set.
openssl pkcs12 -in keystore.pfx -out keystore.pemopenssl rsa -in keystore.pem -RSAPublicKey_out > keystore_public.txtIf you paste your base64 encoded Signed Jwt into https://jwt.io/, you can then paste the contents of your public key file to verify that your Jwt is properly signed. This is what Keycloak will do to verify that you signed your Jwt.
The above method of using appsettings.json to store the private key is not recommended for production. You will want to store your private key in a vault or secure area, and retrieve it in code to sign the JWT.
Copyright © 2021 Province of British Columbia. All rights reserved.