Skip to the content

How to request to GG from eForms

There are some situations when it is necessery to communication with gateway from eForms. Creating of the communication is very easy. Now, we explain you how do it and we add some tips for the best practices.

Prerequisites

  1. Neurodot eForm Designer
  2. Library Newtonsoft.Json
  3. Knowledge the values for communication with GG
    • BootstrapToken - value of Bootstrap Token
    • AppliesTo - URL address for which you want to release ActAs Token
    • ActAsUrl - URL address of GG where is Bootstrap Token transformed to ActAs Token
    • SubmitUrl - URl address of GG where is processed your request
  4. MS Visual Studio - Visual Studio is used for creating the library with code for communication with GG and heplers. 

MS Visual Studio is one of the best practices. You can create the code and helpers in Neurodot eForms Designer, but it is very more comfortable to create it in MS Visual Studio. If you create the external library for eForms, you can test it still before you use it in eForms (for example: you can create a console application and you can debug your external library on your computer), because the debuging is not available in eForms Designer. 

 

How to get values for communication with GG

BootstrapToken

BootstrapToken is saved in your credentials. The hosting web must save it for eForms.

Read BootstrapToken and save it into variable on html page

<%
    var bootstrapContext = System.Security.Claims.ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext;
    var bToken = "";
    if (bootstrapContext != null)
    {
        bToken = Convert.ToBase64String(Encoding.UTF8.GetBytes(bootstrapContext.Token));
    }
%>
<script>
        var bootstrapToken = "<%= bToken %>";
</script>

ActAsUrl, SubmitUrl and AppliesTo

These other values have to be specified in hosting web for example in web.config. 

Sample for loading values from web.config

<script>
        var submitUrl = "<%= ConfigurationManager.AppSettings["GGSubmissionEndpoint"]%>";
        var appliesTo = "<%= ConfigurationManager.AppSettings["GGSubmissionAppliesTo"]%>";
        var actasUrl = "<%= ConfigurationManager.AppSettings["GGFederationActAsEndpoint"]%>";
</script>

Here are sample of values

Name Value
GGSubmissionEndpoint https://<gg address>/ws/submission/rest.svc/submit
GGSubmissionAppliesTo https://<gg address>/ws/submission/public.svc/token
GGFederationActAsEndpoint https://<gg address>/FPSTS/oauth2/actas

Save values to session for eForms

Now, we have required values in hosting page and we need to save them to the session for eForms to be available in eForm ND code. This action is very easy. You have to save the values before you open new eForm. 

<script>
  function SaveDataForForm() {
         SetSessionData('SECURITY_TOKEN', bootstrapToken, function () { });                    
         SetSessionData('SUBMIT_URL', submitUrl, function () { });
         SetSessionData('APPLIES_TO', appliesTo, function () { });
         SetSessionData('ACTAS_URL', actasUrl, function () { });
   }
</script>

This sample saves the required values (SubmitUrl, BootstrapToken ... ) every time when you open new eForm. It is no problem, but every eForms don't need these values. Better solution for it is, you manage the saving by metadata of the eForm which you will want to open. 

Loading required values in eForm

eForms support loading data from a session. You can use command "ThisForm.GetSessionData("<name of parameter>")" The values are loaded in event handler for event Forms.Loaded. 

static string submitUrl =  "";
static string appliesTo = "";
static string actasUrl =  "";
static string btoken = "";

public static void Loaded(object sender, System.Windows.RoutedEventArgs e)
{
	try
	{
		submitUrl = ThisForm.GetSessionData("SUBMIT_URL");
		if(string.IsNullOrWhiteSpace(submitUrl))
		{
			// TODO : data is empty, show some error message
			return;
		}
		appliesTo = ThisForm.GetSessionData("APPLIES_TO");
		if(string.IsNullOrWhiteSpace(appliesTo))
		{	
                        // TODO : data is empty, show some error message		
			return;
		}
		actasUrl = ThisForm.GetSessionData("ACTAS_URL");
		if(string.IsNullOrWhiteSpace(actasUrl))
		{	
		        // TODO : data is empty, show some error message
		        return;
		}
		
		if(string.IsNullOrWhiteSpace(ThisForm.ClientSecurityToken)) //access to bootstrapToken
		{	
		        // TODO : data is empty, show some error message
			return;
		}

		btoken = Encoding.UTF8.GetString(Convert.FromBase64String(ThisForm.ClientSecurityToken));
				
	}
	catch (Exception ex)
	{
		ShowErrorForm(ex);
	}
}

Get ActAs Token (transformation BootstrapToken)

You need ActAs Token for requesting to GG. ActAs Token is released from Bootstrap Token. Library Newtonsoft.Json is required (The response is JWT and it is comfortable to used Newtonsoft.Json for working with it).

 

Here is code for releasing the ActAs Token

using Newtonsoft.Json.Linq;

public static string GetActAsToken(string bootstrapToken, string audience, string actasUrl)
{
      try
       {
            var dic = new System.Collections.Specialized.NameValueCollection();
            dic.Add("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange");
            dic.Add("audience", audience);
            dic.Add("subject_token", bootstrapToken);
            dic.Add("subject_token_type", "urn:ietf:params:oauth:token-type:jwt");

             string actAsToken = null;
             using (WebClient client = new WebClient())
              {
                  client.Headers[System.Net.HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
                  var response = client.UploadValues(actasUrl, dic);
                  var jObject = JObject.Parse(System.Text.Encoding.UTF8.GetString(response));
                  actAsToken = jObject["access_token"].ToString();
              }

              return actAsToken;
        }
        catch (Exception e)
        {
              throw e;
        }
 }

It is better to create a class GGTool in an external library and there placed the function GetActAsToken. You can call the fuction from the external library in the ND code of the eForm.

Check an expiration of ActAs Token

ActAs Token contains more information and one of them is date of expiration. You should check the expiration of ActAs Token before you use it to the request to GG. The reason why uses the validation the token is very simple. You reduce a count of the releasing of the tokens and it reduces the time. Library Newtonsoft.Json is required (The response is JWT and it is comfortable to used Newtonsoft.Json for working with it). 

using Newtonsoft.Json.Linq;

public static bool ValidateActAsToken(string actAsToken)
{
      try
       {
           if (string.IsNullOrWhiteSpace(actAsToken))
              return false;

              var parts = actAsToken.Split('.');

              string partToConvert = parts[1];
              partToConvert = partToConvert.Replace('-', '+');
              partToConvert = partToConvert.Replace('_', '/');
              switch (partToConvert.Length % 4)
              {
                  case 0:
                      break;
                  case 2:
                      partToConvert += "==";
                      break;
                  case 3:
                      partToConvert += "=";
                      break;
              }

              var partAsBytes = Convert.FromBase64String(partToConvert);
              var partAsUTF8String = Encoding.UTF8.GetString(partAsBytes, 0, partAsBytes.Count());

              var jwtToken = JObject.Parse(partAsUTF8String);

              var exp = jwtToken["exp"].Value();

              var expDateTime = new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(exp);

              return DateTime.UtcNow < expDateTime;

       }
       catch (Exception e)
       {
           throw e;
       }
       return false;
}

It is better to create a class GGTool in an external library and there placed the function ValidateActAsToken. You can call the fuction from the external library in the ND code of the eForm.

Create request to GG

Now, we have all credentionals and values for request to GG, but we don't still have a customer message. The message is XML or JSON format because we use JWT we have to use JSON format. 

Define or create customer message

This is my testing message (the message is fiction, only for this tutorial). You have to use a message for your service. It is very importat to have correct values like Class, @productName, @version, ... 

{
   "$schema":"http://www.govtalk.gov.uk/CM/envelope",
   "GovTalkMessage":{
      "EnvelopeVersion":"2.0",
      "Header":{
         "MessageDetails":{
            "Class":"<service class name>",
            "Qualifier":"request",
            "Function":"submit"
         }
      },
      "Body":{
         "$schema":"<request schema>",
         "Message":{
            "Header":{
               "Owner":{
                  "@productName":"<service name>",
                  "@version":"<version>"
               }
            },
            "Data":{
               "@encrypted":"no",
               "@gzip":"no",
               "Request":{
                  "@id":"<request name>",
                  "RequestDetails":{
                     "MyMessage":"Tutorial message"
                  }
               }
            }
         }
      }
   }
}

Sending the request to GG

The main sending your message is now very simple. The function returns a response from GG in JSON format.

public static string SendMessage(string uri, string token, string jsonMsg)
{
     var client = new HttpClient();

     client.DefaultRequestHeaders.Add("Authorization", token);
     var content = new StringContent(jsonMsg, Encoding.UTF8, "application/json");

     var result = client.PostAsync(uri, content).Result;
     string jsonRes = result.Content.ReadAsStringAsync().Result;
            
     return jsonRes;
}

It is better to create a class GGTool in an external library and there placed the function SendMessage. You can call the fuction from the external library in the ND code of the eForm.

Summary

That is all. Now, you know all about requesting from eForms to GG. You have to only call the function SendMessage and you get a respose from GG.

Here is sample of ND code of eForm

Here is link to source code of extrenal library: /media/1076/microsoftggtool.zip

The library contains code for conversion GG XML message to JSON and back, codes for reading some values from BootstrapToken ...

Here is eForm project: /media/1079/reqform.zip

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Xml.Linq;
using Neurodot.Forms.Controls;
using Neurodot.Forms.CustomControls.RadioGroup;
using Neurodot.Forms.Render;
using Neurodot.Forms.Render.Interfaces;

using Microsoft.GGTool.Common;
using System.Xml;
using System.Text;

public static partial class FormCodeClass
{
	// To find controls in the current visual tree (i.e. the form) use static methods:
	// object ThisForm.FindElement(string elementName); or 
	// T ThisForm.FindElement<T>(string elementName);

	private static readonly string xmlTemplate = @"
<Message xmlns='#your namespace#'>
  <Header>
    <Owner productName='#product name#' />
  </Header>
  <Data encrypted = 'no' gzip = 'no'>
    <Request id='#request id#'>                 
      <RequestDetails>
        <MyMsg></MyMsg>
     </RequestDetails>  
    </Request>
  </Data>
</Message>".Replace('\'', '"');

	private static string submitUrl =  "";
	private static string appliesTo = "";
	private static string actasUrl =  "";
	private static string btoken = "";
	private static string actAsToken = null;

	public static void Loaded(object sender, System.Windows.RoutedEventArgs e)
	{
		try
		{
			submitUrl = ThisForm.GetSessionData("SUBMIT_URL");
			if(string.IsNullOrWhiteSpace(submitUrl))
			{
				// TODO : data is empty, show some error message
				return;
			}
			appliesTo = ThisForm.GetSessionData("APPLIES_TO");
			if(string.IsNullOrWhiteSpace(appliesTo))
			{	
	                        // TODO : data is empty, show some error message		
				return;
			}
			actasUrl = ThisForm.GetSessionData("ACTAS_URL");
			if(string.IsNullOrWhiteSpace(actasUrl))
			{	
		                // TODO : data is empty, show some error message
		                return;
			}
			
			if(string.IsNullOrWhiteSpace(ThisForm.ClientSecurityToken)) //access to bootstrapToken
			{	
			        // TODO : data is empty, show some error message
				return;
			}
	
			btoken = Encoding.UTF8.GetString(Convert.FromBase64String(ThisForm.ClientSecurityToken));
					
		}
		catch (Exception ex)
		{
			ShowErrorForm(ex.Message); 
		}
	}


	private static bool SubmitRequest(string myMsg, out XmlDocument xmlRespose)
	{
	     var xmlDocTemplate = new XmlDocument();
	     xmlDocTemplate.LoadXml(xmlTemplate);
	
	     xmlRespose = new XmlDocument();
	     try
	     {
	         var node = xmlDocTemplate.GetElementsByTagName("MyMsg");
	         node[0].InnerText = myMsg;
	         string jsonData = GGTemplate.GetJsonGGMessage(xmlDocTemplate);
	
	         xmlRespose = GGTool.SendMessage(submitUrl, actAsToken, jsonData);
	
	         //TODO: here could be validation of the respose
	
	         return true;
	     }
	     catch (GGException ge)
	     {
	         ShowErrorForm(ge.Message);       
	     }
	     catch (Exception e)
	     {
	         ShowErrorForm(e.Message);       
	     }
	     return false;
	}

	public static void Button1_Click(object sender, System.Windows.RoutedEventArgs e)
	{		
		if(!GetActAsToken())	
		{		
			ShowErrorForm("ActAsToken is null");
			return;
		}
		
		var xmlResponse = new XmlDocument();
		
		SubmitRequest("Test message", out xmlResponse);
		
		//TODO: processing of the response
	}
	
	private static bool GetActAsToken()
	{
		if(!GGTool.ValidateActAsToken(actAsToken))
		{		
			
			actAsToken = GGTool.GetActAsToken(btoken, appliesTo, actasUrl );
			if (actAsToken == null)
			{
				return false;
			}
		}
		return true;
	}
	
	private static void ShowErrorForm(string msg)
	{
		//TODO: show error message
	}
}