Reputation: 11
I want to decode a simple JWT Token in Delphi XE5, but when I do it as shown below, the decodedPayload
is missing the final }
of the JSON object.
What am I doing wrong?
function getIssuerFromToken(token: string): string;
var
tokenPayload, decodedTokenPayload, issuer: string;
tokenJSONValue: TJSONValue;
tokenJSONWrapper: TKJSONWrapper;
tokenPayloadStartIndex, tokenPayloadEndIndex, tokenPayLoadLength: integer;
begin
tokenPayloadStartIndex := Pos('.', token) + 1;
tokenPayloadEndIndex := Pos('.', token, tokenPayloadStartIndex);
tokenPayLoadLength := tokenPayloadEndIndex - tokenPayloadStartIndex;
tokenPayload := Copy(token, tokenPayloadStartIndex, tokenPayLoadLength);
decodedTokenPayload := TIdDecoderMIME.DecodeString(tokenPayload);
...
Upvotes: 0
Views: 288
Reputation: 596166
Per RFC 7519 for the JSON Web Token spec, a JWT token consists of base64url-encoded parts separated by periods. The base64url encoding is described in RFC 7515 for the JSON Web Signature spec. And according to that spec, the base64 is NOT padded:
Base64url Encoding
Base64 encoding using the URL- and filename-safe character set defined in Section 5 of RFC 4648 [RFC4648], with all trailing '=' characters omitted (as permitted by Section 3.2) and without the inclusion of any line breaks, whitespace, or other additional characters. Note that the base64url encoding of the empty octet sequence is the empty string. (See Appendix C for notes on implementing base64url encoding without padding.)
Since base64 requires the input to be an even multiple of 4 characters, you need to add padding to the base64 data before you decode it. You also need to url-decode the base64 before decoding, too. RFC 7515 even provides an example implementation in Appendix C:
static byte [] base64urldecode(string arg)
{
string s = arg;
s = s.Replace('-', '+'); // 62nd char of encoding
s = s.Replace('_', '/'); // 63rd char of encoding
switch (s.Length % 4) // Pad with trailing '='s
{
case 0: break; // No pad chars in this case
case 2: s += "=="; break; // Two pad chars
case 3: s += "="; break; // One pad char
default: throw new System.Exception(
"Illegal base64url string!");
}
return Convert.FromBase64String(s); // Standard base64 decoder
}
In addition, the JSON that is inside the base64 must be UTF-8 encoded, so you have to account for that as well while decoding the base64. The default byte encoding used by TIdDecoderMIME
is 8-bit not UTF-8, but its DecodeString()
method does have an optional AByteEncoding
parameter so you can specify UTF-8.
So, try something like the following in your code:
function decodeJWTpart(const base64: string): string;
var
s: string;
begin
s := base64;
s := StringReplace(s, '-', '+', [rfReplaceAll]);
s := StringReplace(s, '_', '/', [rfReplaceAll]);
case Length(s) mod 4 of
0: ;
2: s := s + '==';
3: s := s + '=';
else
raise Exception.Create('Illegal base64url string!');
end;
Result := TIdDecoder.DecodeString(s, IndyTextEncoding_UTF8);
end;
function getIssuerFromToken(const token: string): string;
var
tokenPayload, ...: string;
...
begin
...
decodedTokenPayload := decodeJWTpart(tokenPayload);
...
end;
Upvotes: 1