Google API Authentication with OAuth 2.0 in D Language (with Firebase Example)

Accessing Google APIs is a valuable capability for developers, enabling you to integrate powerful Google services into your own applications.
Google provides many examples for connecting to their APIs using OAuth 2.0 with popular languages like JavaScript and Python — but what about D Language?

In this post, I’ll walk you through how to connect to Google APIs using OAuth 2.0 in D Language.

The key to using OAuth 2.0 is obtaining an access token.
Once you have the access token, you can make authenticated requests to protected Google services.

Here’s a quick overview of the process:

With the access token in hand, you can access protected resources, and if needed, request a refresh token to extend the session without re-authenticating.

According to Google’s official guide, you first need to create a signed JWT (JSON Web Token) to request an access token.
Creating the signed JWT requires specifying a scope — meaning, which Google service you want your application to interact with.

For this post, I’m using Firebase as an example, since it’s commonly used and easy to set up.

To request the access token, you’ll need some base information from the service (in this case, Firebase), including:

  • Client email
  • Private key
  • Token URI

You can obtain this information from your Firebase Console by downloading the service account credentials as a JSON file.

In our example, the file looks something like this:

// project-name-firebase-adminsdk-abc123-abc456789.json

{
  "type": "service_account",
  "project_id": "project-name",
  "private_key_id": "123456abcdefg",
  "private_key": "-----BEGIN PRIVATE KEY----- abc \n-----END PRIVATE KEY-----\n",
  "client_email": "firebase-adminsdk@project-name.iam.gserviceaccount.com",
  "client_id": "123456",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/...",
  "universe_domain": "googleapis.com"
}

Now, here’s a simplified D Language application that requests an access token from Google API using the information above:

import std.stdio;
import std.file;
import std.json;
import std.base64;
import std.conv;
import std.datetime;
import std.net.curl;
import std.algorithm;
import std.string;
import std.digest.sha;
import std.array;
import std.exception;

import deimos.openssl.aes;
import deimos.openssl.rsa;
import deimos.openssl.sha;
import deimos.openssl.obj_mac;
import deimos.openssl.bio;
import deimos.openssl.pem;
import deimos.openssl.evp;

void main()
{
	string accessToken = getGoogleAccessToken();
	writeln("accessToken = ", accessToken);
}

string getGoogleAccessToken()
{

	string base64UrlEncode(ubyte[] data)
	{
		string b64 = Base64.encode(data);
		b64 = b64.replace("+", "-").replace("/", "_");
		return b64.stripRight("=");
	}

	EVP_PKEY* loadPrivateKeyFromString(string privateKeyData)
	{
		auto bio = BIO_new_mem_buf(cast(void*)privateKeyData.ptr, cast(int)privateKeyData.length);
		if (bio is null) throw new Exception("OpenSSL Error: Failed to create BIO");        

		auto pkey = PEM_read_bio_PrivateKey(bio, null, null, null);
		if (pkey is null) throw new Exception("OpenSSL Error: Failed to load private key");

		BIO_free(bio);
		return pkey;
	}

	string signRS256(EVP_PKEY* pkey, string message)
	{
		auto mdCtx = EVP_MD_CTX_new();
		if (mdCtx is null) throw new Exception("OpenSSL Error: Failed to create EVP_MD_CTX");

		scope(exit) {
			EVP_MD_CTX_free(mdCtx);
		}

		if (EVP_DigestSignInit(mdCtx, null, EVP_sha256(), null, pkey) != 1) throw new Exception("OpenSSL Error: Failed to initialize digest");

		if (EVP_DigestSignUpdate(mdCtx, message.ptr, message.length) != 1) throw new Exception("OpenSSL Error: Failed to update digest");

		size_t sigLen;
		if (EVP_DigestSignFinal(mdCtx, null, &sigLen) != 1) throw new Exception("OpenSSL Error: Failed to get signature length");

		ubyte[] signature = new ubyte[sigLen];
		if (EVP_DigestSignFinal(mdCtx, signature.ptr, &sigLen) != 1) throw new Exception("OpenSSL Error: Failed to sign message");

		return base64UrlEncode(signature[0 .. sigLen]);
	}

	string createJwt(string header, string payload, EVP_PKEY* privateKey)
	{
		string encodedHeader = base64UrlEncode(cast(ubyte[])header);
		string encodedPayload = base64UrlEncode(cast(ubyte[])payload);

		string message = encodedHeader ~ "." ~ encodedPayload;

		string encodedSignature = signRS256(privateKey, message);

		return message ~ "." ~ encodedSignature;
	}
	
	auto serviceAccountJson = parseJSON(cast(string) read("path/to/firebase.json"));
         
	string clientEmail = serviceAccountJson["client_email"].str;
	string privateKeyString = serviceAccountJson["private_key"].str;
	string tokenUri = serviceAccountJson["token_uri"].str;

	auto now = Clock.currTime();

	JSONValue header = JSONValue(["alg": "RS256", "typ": "JWT"]);
	JSONValue payload = JSONValue([
		"iss": clientEmail,
		"scope": "https://www.googleapis.com/auth/firebase.messaging",
		"aud": tokenUri,
		"iat": to!string( now.toUnixTime() ),
		"exp": to!string( (now + 3600.seconds).toUnixTime() )
	]);

	string headerJson = header.toString();
	string payloadJson = payload.toString();
	
	EVP_PKEY* privateKey = loadPrivateKeyFromString(privateKeyString);

	string jwt = createJwt(headerJson, payloadJson, privateKey);
	
	string url = "https://oauth2.googleapis.com/token";
	string postData = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=" ~ jwt;
	
	auto http = HTTP();
	http.addRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	auto response = post(url, postData, http);

	JSONValue json = parseJSON(response);

	writeln("\n postData: ", postData, "\n");
	writeln("\n response: ", response, "\n");

	return json["access_token"].str;

}

Important Dependency: OpenSSL Library

In order to run this application, you’ll need an OpenSSL wrapper for D Language. I used Deimos OpenSSL in this example.

You can try installing it via:

dub add openssl

You may encounter an error like this:

.\app.d(14): Error: unable to read module `aes`
.\app.d(14):        Expected 'deimos\openssl\aes.d' or 'deimos\openssl\aes\package.d' in one of the following import paths:

This error means that the compiler couldn’t find the OpenSSL libraries on your machine.

To resolve this, you need to explicitly tell the DMD compiler where to find the Deimos source files and also specify the required OpenSSL linkages.

You can compile your application with a command like this:

$ dmd -I/usr/local/include/openssl/source -L-lssl -L-lcrypto app.d

This assumes you have the Deimos package source code located at:

After compiling with the command above and running your application, you should see the full request and response, along with the access token retrieved from Google!


If you found this post helpful, please consider sharing it!
Your support helps us continue creating more D Language tutorials and developer content.