I am integrating Xero accounting with my Delphi app. Xero have a VS C# example program, but I've never used C#.
The Xero docs say: The “code challenge” is created by performing a SHA256 hash on the code verifier and then Base64url encoding the hash
The C# code is:
private void btnGenerateLink_Click(object sender, EventArgs e)
{
//construct the link that the end user will need to visit in order to authorize the app
var clientId = txtBoxClientID.Text;
var scopes = Uri.EscapeUriString(txtBoxScopes.Text);
var redirectUri = txtBoxRedirectURI.Text;
var state = txtBoxState.Text;
//generate the code challenge based on the verifier
string codeChallenge;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(txtBoxCodeVerifier.Text));
codeChallenge = Convert.ToBase64String(challengeBytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
}
var authLink = $"{AuthorisationUrl}?response_type=code&client_id={clientId}&redirect_uri={redirectUri}&scope={scopes}&state={state}&code_challenge={codeChallenge}&code_challenge_method=S256";
txtBoxAuthLink.Text = authLink;
btnAuthorise.Enabled = true;
}
How do I re-write this in Delphi 11? I have tried the following:
var
inputKey, hash : string;
b64Encoded : string;
b64url : TBase64UrlEncoding;
begin
//create the code challenge
inputKey := eCodeVerifier.Text;
hash := THashSHA2.GetHashString(inputKey, SHA256);
b64url := TBase64UrlEncoding.Create;
b64Encoded := b64url.Encode(hash);
Not an exact translation of the presented C# code, but hopefully answering your underlying question: here is a function that, given a PKCE code verifier, returns the SHA256 code challenge:
uses
System.SysUtils, System.Hash, System.NetEncoding;
function GetCodeChallenge(const ACodeVerifier: string): string;
var
LSHA256: string;
LSHA256Bytes: TBytes;
begin
LSHA256Bytes := THashSHA2.GetHashBytes(ACodeVerifier, THashSHA2.TSHA2Version.SHA256);
LSHA256 := TNetEncoding.Base64.EncodeBytesToString(LSHA256Bytes);
// The following three lines are what makes
// a "Base64 encoding" into a "Base 64 URL encoding" - no more magic than that
Result := StringReplace(LSHA256, '=', '', [rfReplaceAll]);
Result := StringReplace(Result, '+', '-', [rfReplaceAll]);
Result := StringReplace(Result, '/', '_', [rfReplaceAll]);
end;
Also worth mentioning, perhaps, is that a PKCE code verifier must be a random string of length min 43 and max 128.