Zig Requires Chilkat v11.0.0+
Zig
ING Open Banking OAuth2 Client Credentials
See more OAuth2 Examples
Demonstrates how to get an access token for the ING Open Banking APIs using client credentials.Chilkat Zig Downloads
const std = @import("std");
const chilkat = @import("chilkat");
pub fn main(init: std.process.Init) !void {
const alloc = init.arena.allocator();
// This example requires the Chilkat API to have been previously unlocked.
// See Global Unlock Sample for sample code.
const cert = try chilkat.Cert.init();
defer cert.deinit();
cert.loadFromFile("qa_data/certs_and_keys/ING/example_client_tls.cer") catch {
std.debug.print("{s}\n", .{try cert.getLastErrorText(alloc)});
return;
};
const bd_priv_key = try chilkat.BinData.init();
defer bd_priv_key.deinit();
bd_priv_key.loadFile("qa_data/certs_and_keys/ING/example_client_tls.key") catch {
std.debug.print("Failed to load example_client_tls.key\n", .{});
return;
};
// The OAuth 2.0 client_id for these certificates is e77d776b-90af-4684-bebc-521e5b2614dd.
// Please note down this client_id since you will need it in the next steps to call the API.
const priv_key = try chilkat.PrivateKey.init();
defer priv_key.deinit();
priv_key.loadAnyFormat(bd_priv_key, "") catch {
std.debug.print("{s}\n", .{try priv_key.getLastErrorText(alloc)});
return;
};
// Associate the private key with the certificate.
cert.setPrivateKey(priv_key) catch {
std.debug.print("{s}\n", .{try cert.getLastErrorText(alloc)});
return;
};
const http = try chilkat.Http.init();
defer http.deinit();
http.setSslClientCert(cert) catch {
std.debug.print("{s}\n", .{try http.getLastErrorText(alloc)});
return;
};
// Calculate the Digest and add the "Digest" header. Do the equivalent of this:
// payload="grant_type=client_credentials"
// payloadDigest=`echo -n "$payload" | openssl dgst -binary -sha256 | openssl base64`
// digest=SHA-256=$payloadDigest
const crypt = try chilkat.Crypt2.init();
defer crypt.deinit();
crypt.setHashAlgorithm("SHA256");
crypt.setEncodingMode("base64");
const payload = "grant_type=client_credentials";
const payload_digest = try crypt.hashStringENC(alloc, payload);
// Calculate the current date/time and add the Date header.
// reqDate=$(LC_TIME=en_US.UTF-8 date -u "+%a, %d %b %Y %H:%M:%S GMT")
const dt = try chilkat.DateTime.init();
defer dt.deinit();
dt.setFromCurrentSystemTime() catch {};
// The desire date/time format is the "RFC822" format.
http.setRequestHeader("Date", try dt.getAsRfc822(alloc, false));
// Calculate signature for signing your request
// Duplicate the following code:
// httpMethod="post"
// reqPath="/oauth2/token"
// signingString="(request-target): $httpMethod $reqPath
// date: $reqDate
// digest: $digest"
// signature=`printf "$signingString" | openssl dgst -sha256 -sign "${certPath}example_client_signing.key" -passin "pass:changeit" | openssl base64 -A`
const http_method = "POST";
const req_path = "/oauth2/token";
const sb_string_to_sign = try chilkat.StringBuilder.init();
defer sb_string_to_sign.deinit();
sb_string_to_sign.append("(request-target): ") catch {};
sb_string_to_sign.append(http_method) catch {};
sb_string_to_sign.toLowercase() catch {};
sb_string_to_sign.append(" ") catch {};
sb_string_to_sign.appendLine(req_path, false) catch {};
sb_string_to_sign.append("date: ") catch {};
sb_string_to_sign.appendLine(try dt.getAsRfc822(alloc, false), false) catch {};
sb_string_to_sign.append("digest: SHA-256=") catch {};
sb_string_to_sign.append(payload_digest) catch {};
const signing_priv_key = try chilkat.PrivateKey.init();
defer signing_priv_key.deinit();
signing_priv_key.loadPemFile("qa_data/certs_and_keys/ING/example_client_signing.key") catch {
std.debug.print("{s}\n", .{try signing_priv_key.getLastErrorText(alloc)});
return;
};
const rsa = try chilkat.Rsa.init();
defer rsa.deinit();
rsa.usePrivateKey(signing_priv_key) catch {
std.debug.print("{s}\n", .{try rsa.getLastErrorText(alloc)});
return;
};
rsa.setEncodingMode("base64");
const b64_signature = try rsa.signStringENC(alloc, try sb_string_to_sign.getAsString(alloc), "SHA256");
const sb_auth_hdr_val = try chilkat.StringBuilder.init();
defer sb_auth_hdr_val.deinit();
sb_auth_hdr_val.append("Signature keyId=\"e77d776b-90af-4684-bebc-521e5b2614dd\",") catch {};
sb_auth_hdr_val.append("algorithm=\"rsa-sha256\",") catch {};
sb_auth_hdr_val.append("headers=\"(request-target) date digest\",") catch {};
sb_auth_hdr_val.append("signature=\"") catch {};
sb_auth_hdr_val.append(b64_signature) catch {};
sb_auth_hdr_val.append("\"") catch {};
const sb_digest_hdr_val = try chilkat.StringBuilder.init();
defer sb_digest_hdr_val.deinit();
sb_digest_hdr_val.append("SHA-256=") catch {};
sb_digest_hdr_val.append(payload_digest) catch {};
// Do the following CURL statement:
// curl -i -X POST "${httpHost}${reqPath}" \
// -H 'Accept: application/json' \
// -H 'Content-Type: application/x-www-form-urlencoded' \
// -H "Digest: ${digest}" \
// -H "Date: ${reqDate}" \
// -H "authorization: Signature keyId=\"$keyId\",algorithm=\"rsa-sha256\",headers=\"(request-target) date digest\",signature=\"$signature\"" \
// -d "${payload}" \
// --cert "${certPath}tlsCert.crt" \
// --key "${certPath}tlsCert.key"
const req = try chilkat.HttpRequest.init();
defer req.deinit();
req.addParam("grant_type", "client_credentials");
req.addHeader("Accept", "application/json");
req.addHeader("Date", try dt.getAsRfc822(alloc, false));
req.addHeader("Digest", try sb_digest_hdr_val.getAsString(alloc));
req.addHeader("Authorization", try sb_auth_hdr_val.getAsString(alloc));
req.setHttpVerb("POST");
req.setContentType("application/x-www-form-urlencoded");
const resp = try chilkat.HttpResponse.init();
defer resp.deinit();
http.httpReq("https://api.sandbox.ing.com/oauth2/token", req, resp) catch {
std.debug.print("{s}\n", .{try http.getLastErrorText(alloc)});
return;
};
// If successful, the status code = 200
std.debug.print("Response Status Code: {d}\n", .{resp.getStatusCode()});
std.debug.print("{s}\n", .{try resp.getBodyStr(alloc)});
const json = try chilkat.JsonObject.init();
defer json.deinit();
json.load(try resp.getBodyStr(alloc)) catch {};
json.setEmitCompact(false);
std.debug.print("{s}\n", .{try json.emit(alloc)});
// A successful response contains an access token such as:
// {
// "access_token": "eyJhbGc ... bxI_SoPOBH9xmoM",
// "expires_in": 905,
// "scope": "payment-requests:view payment-requests:create payment-requests:close greetings:view virtual-ledger-accounts:fund-reservation:create virtual-ledger-accounts:fund-reservation:delete virtual-ledger-accounts:balance:view",
// "token_type": "Bearer",
// "keys": [
// {
// "kty": "RSA",
// "n": "3l3rdz4...04VPkdV",
// "e": "AQAB",
// "use": "sig",
// "alg": "RS256",
// "x5t": "3c396700fc8cd709cf9cb5452a22bcde76985851"
// }
// ],
// "client_id": "e77d776b-90af-4684-bebc-521e5b2614dd"
// }
// Use this online tool to generate parsing code from sample JSON:
// Generate Parsing Code from JSON
var kty: [:0]const u8 = "";
var n: [:0]const u8 = "";
var e: [:0]const u8 = "";
var use: [:0]const u8 = "";
var alg: [:0]const u8 = "";
var x5t: [:0]const u8 = "";
_ = try json.stringOf(alloc, "access_token");
_ = json.intOf("expires_in");
_ = try json.stringOf(alloc, "scope");
_ = try json.stringOf(alloc, "token_type");
_ = try json.stringOf(alloc, "client_id");
var i: i32 = 0;
const count_i = json.sizeOfArray("keys");
while (i < count_i) {
json.setI(i);
kty = try json.stringOf(alloc, "keys[i].kty");
n = try json.stringOf(alloc, "keys[i].n");
e = try json.stringOf(alloc, "keys[i].e");
use = try json.stringOf(alloc, "keys[i].use");
alg = try json.stringOf(alloc, "keys[i].alg");
x5t = try json.stringOf(alloc, "keys[i].x5t");
i = i + 1;
}
// This example will save the JSON containing the access key to a file so that
// a subsequent example can load it and then use the access key for a request, such as to create a payment request.
json.writeFile("qa_data/tokens/ing_access_token.json") catch {};
}