Skip to content

Instantly share code, notes, and snippets.

@alexgwalley
Last active September 7, 2025 10:51
Show Gist options
  • Select an option

  • Save alexgwalley/423c5abaef97ddec6fd026719e6869a2 to your computer and use it in GitHub Desktop.

Select an option

Save alexgwalley/423c5abaef97ddec6fd026719e6869a2 to your computer and use it in GitHub Desktop.
Google OAuth 2.0 Get Access Token - Basic
#define WINDOWS_LEAN_AND_MEAN
#include <winsock2.h>
#include <windows.h>
#include <winhttp.h>
#include <stdio.h>
#include <stdint.h>
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "bcrypt.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "winhttp.lib")
void
GenerateRandomBytes(void* buffer, size_t size)
{
BCryptGenRandom(NULL, buffer, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
}
void
Base64FromBinary(void* out_buffer, size_t* out_size, void* in_buffer, size_t in_buffer_size)
{
BOOL result = CryptBinaryToStringA(in_buffer, in_buffer_size, CRYPT_STRING_BASE64URI | CRYPT_STRING_NOCRLF, out_buffer, (DWORD*)out_size);
// base64 buffer of '=' is not allowed for oauth stuff, so we have to trim it down here
while ((*out_size) > 0 && ((char*)out_buffer)[(*out_size) - 1] == '=') {
*out_size = (*out_size) - 1;
((char*)out_buffer)[(*out_size)] = '\0';
}
}
void
SHA256(void* out_buffer, void* in_buffer, int32_t in_buffer_size)
{
BCRYPT_ALG_HANDLE hash_algorithm = 0;
BCryptOpenAlgorithmProvider(&hash_algorithm, BCRYPT_SHA256_ALGORITHM, NULL, 0);
DWORD hash_obj_length = 0;
DWORD len = 0;
BCryptGetProperty(hash_algorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hash_obj_length, sizeof(DWORD), &len, 0);
PUCHAR hash_object = (PUCHAR)HeapAlloc(GetProcessHeap(), 0, hash_obj_length);
BCRYPT_HASH_HANDLE out_hash = NULL;
BCryptCreateHash(hash_algorithm, &out_hash, hash_object, hash_obj_length, NULL, 0, 0);
BCryptHashData(out_hash, in_buffer, in_buffer_size, 0);
BCryptFinishHash(out_hash, out_buffer, 32, 0);
if (out_hash) BCryptDestroyHash(out_hash);
if (hash_object) HeapFree(GetProcessHeap(), 0, hash_object);
if (hash_algorithm) BCryptCloseAlgorithmProvider(hash_algorithm, 0);
}
int main()
{
// IMPORTANT(agw): You need to put your own client_id and client_secret here.
// make sure to include the .apps.googleusercontent.com on the client_id
// storing the client_secret here really isn't safe and technically shouldn't be
// required with PCKE, however google still requires it for some reason
char* client_id = "";
char* client_secret = "";
unsigned char verifier[128];
unsigned char challenge[128];
size_t verifier_size = 128;
size_t challenge_size = 128;
// Generate pcke (Proof key for Code Exchange)
// Here I am just generating random bytes then using base64 to make the bytes "verifier friendly"
{
// get random bytes
unsigned char random_bytes[48]; // I just picked 48 here so the result length would be between the required 43-128
GenerateRandomBytes(random_bytes, sizeof(random_bytes));
// according to docs code_verifier must be A-Z, a-z, 0-9, -._~ -- 43-128 bytes long
Base64FromBinary(verifier, &verifier_size, random_bytes, sizeof(random_bytes));
// Generate the challenge by SHA256'ing these bytes, technically this is optional but recommended
unsigned char sha256[32]; // SHA256 always has 32 byte result
SHA256(sha256, verifier, verifier_size);
// Base64 encode to put in request
Base64FromBinary(challenge, &challenge_size, sha256, sizeof(sha256));
}
printf("Verifier: %.*s\n", (int)verifier_size, verifier);
printf("Challenge: %.*s\n", (int)challenge_size, challenge);
// - Ask user to open this URL in their browser. Applications could also use ShellExecute to do it automatically
int port = 5781;
printf("\nOpen this URL in a browser:\n");
printf("https://accounts.google.com/o/oauth2/v2/auth?"
"client_id=%s"
"&redirect_uri=http%%3A%%2F%%2Flocalhost%%3A%d%%2F" // %d is hidden in here...
"&response_type=code"
"&scope=https%%3A%%2F%%2Fwww.googleapis.com%%2Fauth%%2Fphotoslibrary.readonly" // IMPORTANT: update scopes here
"&code_challenge_method=S256"
"&code_challenge=%s"
"&access_type=offline\n", client_id, port, challenge);
// Start HTTP server to listen to redirect, accept the authorization code from Google
printf("\n\nStarting Server...");
char code_buf[512];
int code_buf_size = 0;
{
WSADATA data = {};
WSAStartup(MAKEWORD(2, 2), &data);
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in server = {};
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = INADDR_ANY;
bind(listen_socket, (struct sockaddr*)&server, sizeof(server));
listen(listen_socket, SOMAXCONN);
char buffer[2048] = {};
SOCKET client_socket = accept(listen_socket, NULL, NULL);
int recv_size = recv(client_socket, buffer, sizeof(buffer)-1, 0);
buffer[recv_size] = '\0';
printf("\n\n%s", buffer);
printf("%s", buffer);
char* start = strstr(buffer, "GET /?code=");
if (start) {
start += strlen("GET /?code=");
char* end = strchr(start, '&');
if (end) {
size_t len = end - start;
if (len < sizeof(code_buf)) {
strncpy(code_buf, start, len);
code_buf[len] = '\0';
code_buf_size = len;
}
}
}
char* reply = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nYou may close this window.";
send(client_socket, reply, (int)strlen(reply), 0);
closesocket(listen_socket);
closesocket(client_socket);
WSACleanup();
}
// ask to get token from google's oauth2 provider
{
// make post request, add body with code
wchar_t* host = L"oauth2.googleapis.com";
wchar_t* path = L"/token";
char postdata[2048];
snprintf(postdata, sizeof(postdata),
"code=%s"
"&client_id=%s"
"&client_secret=%s"
"&code_verifier=%s"
"&redirect_uri=http%%3A%%2F%%2Flocalhost%%3A5781%%2F"
"&grant_type=authorization_code",
code_buf, client_id, client_secret, verifier);
HINTERNET session = WinHttpOpen(L"OAuth/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
HINTERNET connection = WinHttpConnect(session, host, INTERNET_DEFAULT_HTTPS_PORT, 0);
HINTERNET request = WinHttpOpenRequest(connection, L"POST", path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
wchar_t* headers = L"Content-Type: application/x-www-form-urlencoded";
BOOL success = WinHttpSendRequest(request, headers, -1L, (LPVOID)postdata, (DWORD)strlen(postdata), (DWORD)strlen(postdata), 0);
WinHttpReceiveResponse(request, NULL);
DWORD bytes_to_read = 0;
DWORD bytes_read = 0;
do
{
WinHttpQueryDataAvailable(request, &bytes_to_read);
char* read_buffer = (char*)malloc(bytes_to_read + 1);
ZeroMemory(read_buffer, bytes_to_read + 1);
WinHttpReadData(request, read_buffer, bytes_to_read, &bytes_read);
read_buffer[bytes_read] = '\0';
printf("Token Response: %s\n", read_buffer);
/*
NOTE(agw): example response
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly", // will be the scopes you provided
"token_type": "Bearer"
}
*/
free(read_buffer);
} while (bytes_to_read != 0);
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment