fpJWT
fpJWT unit is a part of FCL-Web package in Free Pascal. It provides implementation for "JSON Web Tokens".
Example for login
Q: Where can I find an example of using JWT for login?
A (by forum member PierceNg):
In HTTP, a JWT is transmitted in request header like this: "Authorization: Bearer <jwt>". The server uses that info to make security decisions.
How is the JWT issued to the HTTP client in the first place? By means of some authentication mechanism.
What authentication mechanism? Depends on client. Different for human driving web browser that is running an SPA and automated client making REST calls, as examples.
jwt:= TJWT.Create(TJWTClaims);
try
jwt.Claims.iss:= issuer;
jwt.Claims.exp:= Now + EncodeTime(0, minutesToExpire, 0, 0)]);
jwt.Claims.sub:= subject;
jwt.JOSE.alg:= 'HS256';
jwt.JOSE.typ:= 'JWT';
finally
....
It should be an authentication component that issues the JWT. I assume your CGI program will consume the JWT. How does the client of your CGI handle issuance and use of the JWT?
Some useful reading:
- https://blog.paolorossi.net/jwt-authentication-with-delphi-part-1/
- https://blog.logrocket.com/jwt-authentication-best-practices/
- https://stackoverflow.com/questions/27067251/where-to-store-jwt-in-browser-how-to-protect-against-csrf
Custom claims
Q: I'm porting an application from Delphi, and with Paolo Rossi JWT implementation is possible to add custom claims. I took a look into the source and couldn't figure out how to do it.
A (forum member PascalDragon): You need to create a descendant of TClaims with the custom claims as properties (please note that the property names will be used as is for the claims, so mind the casing). See the example in /packages/fcl-web/examples/jwt/signrs256.lpr.
// claims
jwt.Claims.exp:=DateTimeToUnix(Now+10);
jwt.Claims.iss:='FPC JWT';
with jwt.Claims as TMyClaims do
Name := 'Myself';
{ ... }
// verify our generated token. This generates a new JWT
VerifyResult:=TMyJWT.ValidateJWT(aInput,Key);
if VerifyResult=Nil then
Raise Exception.Create('No verify resultn, verification failed!');
with VerifyResult.Claims as TMyClaims do
Writeln(Name, ' ', admin);
You could also adjust your TJWT descendant like this so that you can directly use your TClaims descendant:
// A JWT that uses our claims
TMyJWT = Class(TJWT)
private
function GetClaims: TMyClaims;
protected
Function CreateClaims : TClaims; override;
public
property Claims: TMyClaims read GetClaims;
end;
function TMyJWT.GetClaims: TMyClaims;
begin
Result := TMyClaims(inherited Claims);
end;
function TMyJWT.CreateClaims: TClaims;
begin
Result:=TMyClaims.Create;
end;
{ ... }
var
aInput, aPrivateKeyPEM, aPublicKeyPEM: String;
Key: TJWTKey;
JWT : TJWT;
VerifyResult: TMyJWT; { <=== !!!! }
Signer: TJWTSigner;
NewDER: TBytes;
RSAPublic: TX509RSAPublicKey;
RSAPrivate: TX509RSAPrivateKey;
{ ... }
// verify our generated token. This generates a new JWT
VerifyResult:=TMyJWT.ValidateJWT(aInput,Key) as TMyJWT;
if VerifyResult=Nil then
Raise Exception.Create('No verify resultn, verification failed!');
Writeln(VerifyResult.Claims.Name, ' ', VerifyResult.Claims.admin);
Signature generation
(from forum member paweld). In FPC 3.2.2 there is no signature generation yet - so you must either use the component: https://github.com/andre-djsystem/LazJWT, or use fpJWT to generate JOSE and Claims and using https://github.com/Xor-el/HashLib4Pascal generate a signature, e.g.
uses
fpjwt, HlpIHashInfo, HlpConverters, HlpHashFactory, DateUtils, Base64;
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowMessage(GenJwt('xxx', 'yyy', 'password', 60));
end;
function TForm1.GenJwt(issuer, subject, secret_key: String; minutesToExpire: Integer): String;
var
jwt: TJWT;
LHMAC: IHMAC;
barr: TBytes;
s: String;
i: Integer;
begin
jwt := TJWT.Create;
jwt.Claims.iss:= issuer;
jwt.Claims.exp:= DateTimeToUnix(IncMinute(Now, minutesToExpire));
jwt.Claims.sub:= subject;
jwt.JOSE.alg:= 'HS256';
jwt.JOSE.typ:= 'JWT';
Result := jwt.JOSE.AsEncodedString + '.' + jwt.Claims.AsEncodedString;
LHMAC := THashFactory.THMAC.CreateHMAC(THashFactory.TCrypto.CreateSHA2_256);
LHMAC.Key := TConverters.ConvertStringToBytes(secret_key, TEncoding.UTF8);
barr := LHMAC.ComputeString(Result, TEncoding.UTF8).GetBytes();
s := '';
for i := Low(barr) to High(barr) do
s := s + chr(barr[i]);
jwt.Signature := jwt.Base64ToBase64URL(EncodeStringBase64(s));
Result := Result + '.' + jwt.Signature;
jwt.Free;
end;
(from forum member WayneSherman). JWT signing was added to FPC trunk in 2022: