Authentication with the Workflow REST API

This topic provides some samples that demonstrate how to use different authentication approaches with the Workflow REST API.

In the code samples below, you will need to replace values in square brackets with the appropriate values for your environment. For example, the value [K2RESTSERVICEBASEURL] refers to the location of the Site where the service is hosted. You can determine the REST service's URL for you environment through the Management Site by navigating to IntegrationAPIsWorkflow REST, the URL will be displayed in the Base URL property. The URL may look something like this, of course the value would be different for your environment: https://k2serverprod:4444/api/workflow/v1
The Workflow REST API only supports basic and OAuth authentication.

There are many ways of working with authentication and OAuth in Azure apps. This topic covers the basics and you can find many more examples on the internet.

Samples:

If using OAuth authentication in your code, you will need to configure your environment for OAuth. Please see the KB article KB002784: Configure AAD and K2 Services for Inbound OAuth for details.

Basic Authentication

This example shows how to use Basic Authentication with the Workflow REST API. Note that basic authentication is not as secure as OAuth, and the connection will always be authenticated by the product using the context of the username and password you pass in.

Copy

Basic Authentication

// Create a new HttpClient object as static for the application, make sure it's only created once to avoid opening multiple ports on a server, per application.
// Dispose of httpclient object when application closes.
static System.Net.Http.HttpClient k2WebClient;

public void BasicAuth()
{
    // Passing static credentials for authentication, using a client handler to store credentials.
    System.Net.NetworkCredential k2credentials = new NetworkCredential("[USERNAME]", "[PASSWORD]");
    System.Net.Http.HttpClientHandler loginHandler = new HttpClientHandler
    {
        Credentials = k2credentials
    };

    // Open the HTTPClient connection one time upon form load so that we only open one port/socket on the server.
    k2WebClient = new System.Net.Http.HttpClient(loginHandler, true);
}

Windows Integrated Authentication

To configure the Workflow REST API to use Windows Integrated authentication, enable Windows Authentication in IIS for the Workflow API.

  1. Open IIS and browse to the API (K2 > API > Workflow > v1)
  2. Open Authentication
  3. Enable Windows Authentication

Any code you use must have the UseDefaultCredentials property set to true.

Copy

UseDefaultCredentials = true

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://[k2environmentid]/api/workflow/v1/Employees");            

//Use the currently logged on user for authentication
request.UseDefaultCredentials = true;                        

//execute the request
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

//Process response
...

OAuth: Static credentials using ClientID and Secret Key (C#)

This code snippet demonstrates how to construct an OAuth token in C#, without prompting for a username and password. Note that a statically-defined username and password is used.

Hard-coding the Client ID, Client Secret, Username and Password is a security risk, rather use a secure store.
Copy

OAuth: Static credentials

public async static void OAuthSampleNoPrompt()
{
    string username = "[username]@[yourdomain].onmicrosoft.com"; //TODO: replace with the username and domain with your details
    string password = "[password]"; //TODO: replace with the user's password
    var vals = new List<KeyValuePair<string,
     string>> {
   new KeyValuePair < string,
   string > ("grant_type", "password"),
   new KeyValuePair < string,
   string > ("scope", "openid"),
   new KeyValuePair < string,
   string > ("resource", "https://api.k2.com/"), //Identifier of the target resource that is the recipient of the requested token. This value will most likely be https://api.k2.com/ in your environment.
   //Hard-coding the Client ID, Client Secret, Username and Password is a security risk, rather use a secure store.
   new KeyValuePair < string,
   string > ("client_id", "[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]"), //Client ID of the custom client app requesting the token (the app should have the K2 API permission scope). If using AAD for OAuth, provide the Application ID of your custom app registration; you can find this value in the Application ID field in the App Registration page, in Settings > Properties
   new KeyValuePair < string,
   string > ("client_secret", "[###################################]"), //TODO: the secret key for the custom client app requesting the token. If using AAD for OAuth, provide the Client Secret (also known as the Key value) from your AAD App Registration page, in the Settings > Keys page
   new KeyValuePair < string,
   string > ("username", username),
   new KeyValuePair < string,
   string > ("password", password)
  };

    var url = $"https://login.microsoftonline.com/[uniqueID]/oauth2/token"; //TODO: use the outh2token endpoint URL from your AAD config

    var hc = new HttpClient();
    HttpContent hcContent = new FormUrlEncodedContent(vals);
    HttpResponseMessage hcResponse = hc.PostAsync(url, hcContent).Result;

    if (!hcResponse.IsSuccessStatusCode) return;
    //read in the token that was returned
    System.IO.Stream data = await hcResponse.Content.ReadAsStreamAsync();
    string responseData;
    using (var reader = new System.IO.StreamReader(data, Encoding.UTF8))
    {
        responseData = reader.ReadToEnd();
    }

    //construct an access token object using the NewtonSoft Json helper
    AccessToken authToken = Newtonsoft.Json.JsonConvert.DeserializeObject<AccessToken>(responseData);

    //set up the endpoint to hit
    string fullUrl = ("https://[k2environmentid]/api/workflow/v1/workflows"); //TODO: replace with your K2 environment's URL and the endpoint you want to hit
    System.Net.WebRequest request = WebRequest.Create(fullUrl);
    //pass in the outh token you obtained
    request.Headers.Add("Authorization", "Bearer " + authToken.Access_Token);

    System.IO.Stream dataStream = null;
    string result;

    try
    {
        var webresponse = (HttpWebResponse)request.GetResponse();
        int webresponseCode = (int)(webresponse).StatusCode;

        dataStream = webresponse.GetResponseStream();
        System.Text.Encoding encode = System.Text.Encoding.GetEncoding("UTF-8");
        System.IO.StreamReader readStream = new System.IO.StreamReader(dataStream, encode);

        result = readStream.ReadToEnd();

        webresponse.Close();
    }
    finally
    {
        if (dataStream != null)
            dataStream.Dispose();
    }
    Debug.WriteLine(result);
}

class AccessToken
{
    //represents a public string token_type;
    public string Scope { get; set; }
    public string ExpiresIn { get; set; }
    public string ExpiresOn { get; set; }
    public string NotBefore { get; set; }
    public string Resource { get; set; }
    public string Access_Token { get; set; }
    public string RefreshToken { get; set; }
    public string IdToken { get; set; }
}

OAuth: Static credentials using ClientID and Secret Key (JavaScript)

This code snippet demonstrates how to construct an OAuth token in JavaScript, without prompting for a username and password. Note that a statically-defined username and password is used.

Hard-coding the Client ID, Client Secret, Username and Password is a security risk, rather use a secure store.
Copy

OAuth: Static Credentials in JavaScript

// OAuth: Static Credentials
// This sample uses the OAuth Resource Owner Password grant
// Will only work with Azure Active Directory
// Will not work with users with MFA enabled

var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

// TODO: Directory (tenant) ID from AAD
const tenantId = "[uniqueID]";

// Hard-coding the Client ID, Client Secret, Username and Password is a security risk, rather use a secure store
const vals = {
  grant_type: "password",
  scope: "openid",
  resource: "https://api.k2.com/",
  // TODO: Application (client) ID from AAD, must have K2 API permission scope
  client_id: "[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]",
  // TODO: Secret for the client ID
  client_secret: "[###################################]",
  // TODO: Azure AD user account
  username: "[username]@[yourdomain].onmicrosoft.com",
  // TODO: User's password
  password: "[password]",
};

// TODO: Set K2 Environment URL
const environmentUrl = "https://[k2environmentid]/api/workflow/v1/workflows";

function GetUserToken(callback) {
  var formBody = [];
  for (var property in vals) {
    var encodedKey = encodeURIComponent(property);
    var encodedValue = encodeURIComponent(vals[property]);
    formBody.push(`${encodedKey}=${encodedValue}`);
  }
  formBody = formBody.join("&");

  var request = new XMLHttpRequest();
  request.onreadystatechange = function () {
    if (request.readyState == 4 && request.status == 200) {
      json = JSON.parse(this.responseText);
      callback(json["access_token"]);
    } else {
      console.log(this.responseText);
    }
  };

  request.open(
    "POST",
    `https://login.microsoftonline.com/${tenantId}/oauth2/token`
  );

  request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  request.send(formBody);
}

function GetWorkflows(token) {
  var request = new XMLHttpRequest();
  request.onreadystatechange = function () {
    console.log(this.responseText);
  };

  request.open("GET", environmentUrl);
  request.setRequestHeader("Content-Type", "application/json");
  request.setRequestHeader("Authorization", `Bearer ${token}`);
  request.send();
}

GetUserToken((token) => GetWorkflows(token));

OAuth: Prompt for credentials

This code snippet shows how to get an OAuth token in C#, where the user gets prompted to login through the AAD Sign-on page if a token isn't found.

Copy

OAuth: Prompt for credentials

Uri _baseUrl = new Uri("https://[environmentid]/api"); //TODO: replace with your K2 environment's URL
string _clientId = "[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]"; //Client ID of the custom client app requesting the token (the app should have the K2 API permission scope). If using AAD for OAuth, provide the Application ID of your custom app registration; you can find this value in the Application ID field in the App Registration page, in Settings > Properties
string _authority = "https://login.microsoftonline.com/common/login"; //Login URL for AAD
string _redirectUri = @"https://api.k2.com/client"; //Address to return to upon receiving a response from the authority
string _webApiResourceId = @"https://api.k2.com/"; //Identifier of the target resource that is the recipient of the requested token. This value will most likely be https://api.k2.com/ in your environment
string _resource = "workflow/v1/workflows"; //the endpoint you want to query (in this case, a list of workflow definitions)

//set up http client
System.Net.Http.HttpClient _httpClient = new HttpClient(new HttpClientHandler()
{
    UseDefaultCredentials = false,
});

//platform-specific arguments and information
Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters parameters = new PlatformParameters(PromptBehavior.Auto);
//retrieves authentication tokens from Azure Active Directory and ADFS services
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = new AuthenticationContext(_authority);
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult authResult = null;

try 
{
 //get the OAuth token (will show login screen to user if no token found)
 authResult = authContext.AcquireTokenAsync(_webApiResourceId, _clientId, new Uri(_redirectUri), parameters).Result;

catch (Microsoft.IdentityModel.Clients.ActiveDirectory.AdalException adalEx) 
{
 //TODO: do something with the auth error
 return;
}

//use pathos client to construct the HTTP request and read response
Pathoschild.Http.Client.IRequest request = null;
Pathoschild.Http.Client.IResponse result = null;

request = (Pathoschild.Http.Client.IRequest) new Pathoschild.Http.Client.FluentClient(_baseUrl, _httpClient).GetAsync(_resource).WithHttpErrorAsException(false);
//pass in the access token that was obtained
request = request.WithBearerAuthentication(authResult.AccessToken);
result = request.AsResponse().Result;
//get the result from the endpoint call
string returnvalue = result.Message.Content.ReadAsStringAsync().Result;