fpJWT

From Free Pascal wiki
Jump to navigationJump to search

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:

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: