Recent

Author Topic: Is possible to add custom claims to TJWT?  (Read 911 times)

LeoBruno

  • Jr. Member
  • **
  • Posts: 61
Is possible to add custom claims to TJWT?
« on: August 17, 2022, 10:41:22 pm »
Hi:

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.

For now, I´m using an example found on the internet.

fpc code:

Code: Pascal  [Select][+][-]
  1. function GetJWTToken(aEmail: string): TJsonObject;
  2. var
  3.   _JWTToken: TJWT;
  4.   _HMAC: IHMAC;
  5.   _Agora,
  6.   _DtaExpiracao: TDateTime;
  7.   _Assinatura,
  8.   _Token: string;
  9. begin
  10.   _Agora := TModelServerUtils.GetInstancia.GetServerDateTime;
  11.   _JWTToken := TJWT.Create;
  12.   try
  13.     _JWTToken.JOSE.alg := 'HS512';
  14.     _JWTToken.JOSE.typ := 'JWT';
  15.    // _DtaExpiracao := (_Agora + 1);
  16.  
  17.     _DtaExpiracao := (_Agora + 365);
  18.  
  19.     _JWTToken.Claims.iss := 'TestIssuer';
  20.     _JWTToken.Claims.sub := aEmail;
  21.     _JWTToken.Claims.iat := Trunc(_Agora);
  22.     _JWTToken.Claims.exp := Trunc(_DtaExpiracao);
  23.  
  24.     if (_JWTToken.JOSE.alg = 'HS256') then
  25.       _HMAC := THashFactory.THMAC.CreateHMAC(THashFactory.TCrypto.CreateSHA2_256)
  26.     else if (_JWTToken.JOSE.alg = 'HS384') then
  27.       _HMAC := THashFactory.THMAC.CreateHMAC(THashFactory.TCrypto.CreateSHA2_384)
  28.     else if (_JWTToken.JOSE.alg = 'HS512') then
  29.       _HMAC := THashFactory.THMAC.CreateHMAC(THashFactory.TCrypto.CreateSHA2_512)
  30.     else
  31.       raise Exception.Create('[alg] não implementado');
  32.  
  33.     _HMAC.Key := TConverters.ConvertStringToBytes(UTF8Encode(cSecret), TEncoding.UTF8);
  34.     _Assinatura := HexToAscii(TConverters.ConvertBytesToHexString(_HMAC.ComputeString(UTF8Encode(Trim(_JWTToken.AsString)), TEncoding.UTF8).GetBytes,False));
  35.     _Assinatura := _JWTToken.Base64ToBase64URL(EncodeStringBase64(_Assinatura));
  36.  
  37.     _Token := _JWTToken.AsString + '.' + _Assinatura;
  38.  
  39.     result := TJsonObject.Create;
  40.     result.Add('AcessToken', _Token);
  41.     result.Add('ValidUntil', DateTimeToStr(_DtaExpiracao));
  42.   finally
  43.     _JWTToken.Free;
  44.   end;
  45. end;
  46.  

Delphi code (just mind the setclaimsoftype section:
Code: Pascal  [Select][+][-]
  1. function GetJWTToken(aNumCnpjCpf, aCadCompleto: string): string;
  2. var
  3.   _JWTToken: TJWT;
  4.   _DtaExpiracao: TDateTime;
  5. begin
  6.   _JWTToken := TJWT.Create;
  7.   try
  8.     _DtaExpiracao := (Now + 10);
  9.  
  10.     _JWTToken.Claims.Issuer := 'TestIssuer';
  11.     _JWTToken.Claims.Subject := aNumCnpjCpf;
  12.     _JWTToken.Claims.Expiration := _DtaExpiracao;
  13.     _JWTToken.Claims.IssuedAt := Now;
  14.     _JWTToken.Claims.SetClaimOfType<string>('ind_cadastro_completo', aCadCompleto);
  15.  
  16.     result := TJose.SHA256CompactToken(cSecret, _JWTToken);
  17.   finally
  18.     _JWTToken.Free;
  19.   end;
  20. end;
  21.  

Is it possible to do it?

How?

Thank's in advance...


« Last Edit: August 17, 2022, 10:47:45 pm by LeoBruno »
Lazarus 2.2.2 FPC 3.2.2 Windows (qt5) Anchor Docking

PascalDragon

  • Hero Member
  • *****
  • Posts: 4552
  • Compiler Developer
Re: Is possible to add custom claims to TJWT?
« Reply #1 on: August 18, 2022, 01:27:08 pm »
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 $fpc/packages/fcl-web/examples/jwt/signrs256.lpr.

LeoBruno

  • Jr. Member
  • **
  • Posts: 61
Re: Is possible to add custom claims to TJWT?
« Reply #2 on: August 18, 2022, 08:07:12 pm »
Nice!

Thank you!
Lazarus 2.2.2 FPC 3.2.2 Windows (qt5) Anchor Docking

LeoBruno

  • Jr. Member
  • **
  • Posts: 61
Re: Is possible to add custom claims to TJWT?
« Reply #3 on: August 19, 2022, 11:36:47 pm »
Can you give me a hand reading the claims from a generated jwt?

Thank's in advance.

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 $fpc/packages/fcl-web/examples/jwt/signrs256.lpr.
Lazarus 2.2.2 FPC 3.2.2 Windows (qt5) Anchor Docking

PascalDragon

  • Hero Member
  • *****
  • Posts: 4552
  • Compiler Developer
Re: Is possible to add custom claims to TJWT?
« Reply #4 on: August 20, 2022, 11:23:53 pm »
Did you call ValidateJWT using your descendant of TJWT? If so you can then try to cast the Claims property to your TClaims descendant and access the properties.

If that doesn't work for you then please show what you're doing.

LeoBruno

  • Jr. Member
  • **
  • Posts: 61
Re: Is possible to add custom claims to TJWT?
« Reply #5 on: August 21, 2022, 12:51:37 am »
The JWT Token was generated sucessfully.

I want to be able to get the previously generated token in the head section of the request, and grab some claims information from it.

Using Delphi's implementation of JWT by Paolo Rossi, I do it this way:

Code: Pascal  [Select][+][-]
  1.   _JWTToken := TJose.DeserializeCompact(cSecret, _Token);
  2.   try
  3.     result := _JWTToken.Claims.Subject;
  4.   finally
  5.     _JWTToken.Free;
  6.   end;
  7.  



Did you call ValidateJWT using your descendant of TJWT? If so you can then try to cast the Claims property to your TClaims descendant and access the properties.

If that doesn't work for you then please show what you're doing.
Lazarus 2.2.2 FPC 3.2.2 Windows (qt5) Anchor Docking

PascalDragon

  • Hero Member
  • *****
  • Posts: 4552
  • Compiler Developer
Re: Is possible to add custom claims to TJWT?
« Reply #6 on: August 21, 2022, 05:08:20 pm »
I'll be using the mentioned example as a base:

Code: Pascal  [Select][+][-]
  1.   { snip }
  2.  
  3.   // claims
  4.   jwt.Claims.exp:=DateTimeToUnix(Now+10);
  5.   jwt.Claims.iss:='FPC JWT';
  6.   with jwt.Claims as TMyClaims do
  7.     Name := 'Myself';
  8.  
  9.   { snip }
  10.  
  11.   // verify our generated token. This generates a new JWT
  12.   VerifyResult:=TMyJWT.ValidateJWT(aInput,Key);
  13.   if VerifyResult=Nil then
  14.     Raise Exception.Create('No verify resultn, verification failed!');
  15.  
  16.   with VerifyResult.Claims as TMyClaims do
  17.     Writeln(Name, ' ', admin);

You could also adjust your TJWT descendant like this so that you can directly use your TClaims descendant (again, based on the example):

Code: Pascal  [Select][+][-]
  1. { snip }
  2.   // A JWT that uses our claims
  3.   TMyJWT = Class(TJWT)
  4.   private
  5.     function GetClaims: TMyClaims;
  6.   protected
  7.     Function CreateClaims : TClaims; override;
  8.   public
  9.     property Claims: TMyClaims read GetClaims;
  10.   end;
  11.  
  12.  
  13. function TMyJWT.GetClaims: TMyClaims;
  14. begin
  15.   Result := TMyClaims(inherited Claims);
  16. end;
  17.  
  18. function TMyJWT.CreateClaims: TClaims;
  19. begin
  20.   Result:=TMyClaims.Create;
  21. end;
  22.  
  23. { snip }
  24.  
  25. var
  26.   aInput, aPrivateKeyPEM, aPublicKeyPEM: String;
  27.   Key: TJWTKey;
  28.   JWT : TJWT;
  29.   VerifyResult: TMyJWT; { <=== !!!! }
  30.   Signer: TJWTSigner;
  31.   NewDER: TBytes;
  32.   RSAPublic: TX509RSAPublicKey;
  33.   RSAPrivate: TX509RSAPrivateKey;
  34.  
  35. { snip }
  36.  
  37.   // verify our generated token. This generates a new JWT
  38.   VerifyResult:=TMyJWT.ValidateJWT(aInput,Key) as TMyJWT;
  39.   if VerifyResult=Nil then
  40.     Raise Exception.Create('No verify resultn, verification failed!');
  41.   Writeln(VerifyResult.Claims.Name, ' ', VerifyResult.Claims.admin);

LeoBruno

  • Jr. Member
  • **
  • Posts: 61
Re: Is possible to add custom claims to TJWT?
« Reply #7 on: August 25, 2022, 10:33:00 pm »
Hi:

Thank you for the reply.

The method "ValidateJWT" does not exsist on fpjwt unit.

Code: Pascal  [Select][+][-]
  1.  
  2. { **********************************************************************
  3.   This file is part of the Free Component Library (FCL)
  4.   Copyright (c) 2015 by the Free Pascal development team
  5.        
  6.   JSON Web Token implementation
  7.            
  8.   See the file COPYING.FPC, included in this distribution,
  9.   for details about the copyright.
  10.                    
  11.   This program is distributed in the hope that it will be useful,
  12.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14.   **********************************************************************}
  15. unit fpjwt;
  16.  
  17. {$mode objfpc}{$H+}
  18.  
  19. interface
  20.  
  21. uses
  22.   TypInfo, Classes, SysUtils, fpjson, base64;
  23.  
  24. Type
  25.  
  26.   { TBaseJWT }
  27.  
  28.   TBaseJWT = Class(TPersistent)
  29.   private
  30.   Protected
  31.     // Override this to disable writing a property to the JSON.
  32.     function WriteProp(P: PPropInfo; All: Boolean): Boolean; virtual;
  33.     function GetAsEncodedString: String; virtual;
  34.     procedure SetAsEncodedString(AValue: String); virtual;
  35.     function GetAsString: TJSONStringType; virtual;
  36.     procedure SetAsString(AValue: TJSONStringType);virtual;
  37.     Procedure DoLoadFromJSON(JSON : TJSONObject);virtual;
  38.     Procedure DoSaveToJSON(JSON : TJSONObject; All : Boolean);virtual;
  39.   Public
  40.     Constructor Create; virtual;
  41.     Procedure LoadFromJSON(JSON : TJSONObject);
  42.     Procedure SaveToJSON(JSON : TJSONObject; All : Boolean);
  43.     // Base64url conversion functions (RFC7515)
  44.     class function Base64ToBase64URL(AValue: string): string;
  45.     class function Base64URLToBase64(AValue: string): string;
  46.     // Decode Base64url string.
  47.     Class Function DecodeString(S : String) : String;
  48.     // Decode Base64url string and return a JSON Object.
  49.     Class Function DecodeStringToJSON(S : String) : TJSONObject;
  50.     // Get/Set as string. This is normally the JSON form.
  51.     Property AsString : TJSONStringType Read GetAsString Write SetAsString;
  52.     // Set as string. This is normally the JSON form, encoded as Base64.
  53.     Property AsEncodedString : String Read GetAsEncodedString Write SetAsEncodedString;
  54.   end;
  55.  
  56.   { TJOSE }
  57.  
  58.   TJOSE = Class(TBaseJWT)
  59.   private
  60.     Falg: String;
  61.     Fcrit: String;
  62.     Fcty: String;
  63.     Fjku: String;
  64.     Fjwk: String;
  65.     Fkid: String;
  66.     Ftyp: String;
  67.     Fx5c: String;
  68.     Fx5t: String;
  69.     Fx5ts256: String;
  70.     Fx5u: String;
  71.   Published
  72.     // Registered names. Keep the case lowercase, the RTTI must match the registered name.
  73.     Property cty : String Read Fcty Write Fcty;
  74.     Property typ : String Read Ftyp Write Ftyp;
  75.     Property alg : String Read Falg Write Falg;
  76.     Property jku : String Read Fjku Write fjku;
  77.     Property jwk : String Read Fjwk Write fjwk;
  78.     Property kid : String Read Fkid Write fkid;
  79.     Property x5u : String Read Fx5u Write fx5u;
  80.     Property x5c : String Read Fx5c Write fx5c;
  81.     Property x5t : String Read Fx5t Write fx5t;
  82.     Property x5ts256 : String Read Fx5ts256 Write fx5ts256;
  83.     Property crit : String Read Fcrit Write fcrit;
  84.   end;
  85.   TJOSEClass = Class of TJOSE;
  86.  
  87.   { TClaims }
  88.  
  89.   TClaims = Class(TBaseJWT)
  90.   private
  91.     FAud: String;
  92.     FExp: Int64;
  93.     FIat: Int64;
  94.     FIss: String;
  95.     FJTI: String;
  96.     FNbf: Int64;
  97.     FSub: String;
  98.   Published
  99.     // Registered Claim Names. Keep the case lowercase, the RTTI must match the registered name.
  100.     Property iss : String Read FIss Write FIss;
  101.     Property sub : String Read FSub Write FSub;
  102.     Property aud : String Read FAud Write FAud;
  103.     Property exp : Int64 Read FExp Write FExp;
  104.     Property nbf : Int64 Read FNbf Write FNbf;
  105.     Property iat : Int64 Read FIat Write FIat;
  106.     Property jti : String Read FJTI Write FJTI;
  107.   end;
  108.   TClaimsClass = Class of TClaims;
  109.  
  110.   { TJWT }
  111.  
  112.   TJWT = Class(TBaseJWT)
  113.   private
  114.     FClaims: TClaims;
  115.     FJOSE: TJOSE;
  116.     FSignature: String;
  117.     procedure SetClaims(AValue: TClaims);
  118.     procedure SetJOSE(AValue: TJOSE);
  119.   Protected
  120.     Function CreateJOSE : TJOSE; Virtual;
  121.     Function CreateClaims : TClaims; Virtual;
  122.     // AsString and AsEncodedString are the same in this case.
  123.     function GetAsString: TJSONStringType; override;
  124.     procedure SetAsString(AValue: TJSONStringType);override;
  125.     function GetAsEncodedString: String;override;
  126.     Procedure SetAsEncodedString (AValue : String);override;
  127.   Public
  128.     Constructor Create; override;
  129.     Destructor Destroy; override;
  130.     // Owned by the JWT. The JSON header.
  131.     Property JOSE : TJOSE Read FJOSE Write SetJOSE;
  132.     // Owned by the JWT. The set of claims. The actuall class will depend on the descendant.
  133.     Property Claims : TClaims Read FClaims Write SetClaims;
  134.     Property Signature : String Read FSignature Write FSignature;
  135.   end;
  136.  
  137. implementation
  138.  
  139. uses strutils;
  140.  
  141. { TJWT }
  142.  
  143. procedure TJWT.SetClaims(AValue: TClaims);
  144. begin
  145.   if FClaims=AValue then Exit;
  146.   FClaims:=AValue;
  147. end;
  148.  
  149. procedure TJWT.SetJOSE(AValue: TJOSE);
  150. begin
  151.   if FJOSE=AValue then Exit;
  152.   FJOSE:=AValue;
  153. end;
  154.  
  155. function TJWT.CreateJOSE: TJOSE;
  156. begin
  157.   Result:=TJOSE.Create;
  158. end;
  159.  
  160. function TJWT.CreateClaims: TClaims;
  161. begin
  162.   Result:=TClaims.Create;
  163. end;
  164.  
  165. function TJWT.GetAsString: TJSONStringType;
  166. begin
  167.   Result:=Base64ToBase64URL(EncodeStringBase64(JOSE.AsString));
  168.   Result:=Result+'.'+Base64ToBase64URL(EncodeStringBase64(Claims.AsString));
  169.   If (Signature<>'') then
  170.     Result:=Result+'.'+Signature;
  171. end;
  172.  
  173.  
  174. function TJWT.GetAsEncodedString: String;
  175. begin
  176.   Result:=GetAsString;
  177. end;
  178.  
  179. procedure TJWT.SetAsEncodedString(AValue: String);
  180. begin
  181.   SetAsString(AValue);
  182. end;
  183.  
  184. constructor TJWT.Create;
  185. begin
  186.   Inherited;
  187.   FJOSE:=CreateJOSE;
  188.   FClaims:=CreateCLaims;
  189. end;
  190.  
  191. destructor TJWT.Destroy;
  192. begin
  193.   FreeAndNil(FJOSE);
  194.   FreeAndNil(FClaims);
  195.   Inherited;
  196. end;
  197.  
  198. procedure TJWT.SetAsString(AValue: TJSONStringType);
  199.  
  200. Var
  201.   J,C,S : String;
  202.  
  203. begin
  204.   J:=ExtractWord(1,AValue,['.']);
  205.   C:=ExtractWord(2,AValue,['.']);
  206.   S:=ExtractWord(3,AValue,['.']);
  207.   JOSE.AsEncodedString:=J;
  208.   Claims.AsEncodedString:=C;
  209.   Signature:=S;
  210. end;
  211.  
  212. { TBaseJWT }
  213.  
  214. function TBaseJWT.GetAsEncodedString: String;
  215. begin
  216.   Result:=Base64ToBase64URL(EncodeStringBase64(AsString));
  217. end;
  218.  
  219. procedure TBaseJWT.SetAsEncodedString(AValue: String);
  220.  
  221. begin
  222.   AsString:=DecodeString(AValue);
  223. end;
  224.  
  225. function TBaseJWT.GetAsString: TJSONStringType;
  226.  
  227. Var
  228.   O : TJSONObject;
  229.  
  230. begin
  231.   O:=TJSONObject.Create;
  232.   try
  233.     SaveToJSON(O,False);
  234.     Result:=O.AsJSON;
  235.   finally
  236.     O.Free;
  237.   end;
  238. end;
  239.  
  240. procedure TBaseJWT.SetAsString(AValue: TJSONStringType);
  241. Var
  242.   D : TJSONData;
  243.   O : TJSONObject absolute D;
  244.  
  245. begin
  246.   D:=GetJSON(AValue);
  247.   try
  248.     if D is TJSONObject then
  249.       LoadFromJSON(O);
  250.   finally
  251.     D.Free;
  252.   end;
  253. end;
  254.  
  255. procedure TBaseJWT.DoLoadFromJSON(JSON: TJSONObject);
  256.  
  257. Var
  258.   D : TJSONEnum;
  259.   P : PPropinfo;
  260.  
  261. begin
  262.   For D in JSON Do
  263.     begin
  264.     P:=GetPropInfo(Self,D.Key);
  265.     if (P<>Nil) and not D.Value.IsNull then
  266.       Case P^.PropType^.Kind of
  267.         tkInteger : SetOrdProp(Self,P,D.Value.AsInteger);
  268.         tkChar :
  269.             if D.Value.AsString<>'' then
  270.               SetOrdProp(Self,P,Ord(D.Value.AsString[1]));
  271.         tkEnumeration :
  272.           if (D.Value.JSONType=jtNumber) and (TJSONNumber(D.Value).NumberType=ntInteger) then
  273.             SetOrdProp(Self,P,D.Value.AsInteger)
  274.           else
  275.             SetOrdProp(Self,P,GetEnumValue(p^.PropType,D.Value.AsString));
  276.         tkFloat :
  277.           SetFloatProp(Self,P,D.Value.AsFloat);
  278.         tkSString,tkLString,tkAString :
  279.             SetStrProp(Self,P,D.Value.AsString);
  280.         tkWChar, tkUString,tkWString,tkUChar:
  281.             SetWideStrProp(Self,P,D.Value.AsString);
  282.         tkBool :
  283.           SetOrdProp(Self,P,Ord(D.Value.AsBoolean));
  284.         tkInt64,tkQWord:
  285.           SetInt64Prop(Self,P,Ord(D.Value.AsInt64));
  286.         end;
  287.    end;
  288. end;
  289.  
  290. function TBaseJWT.WriteProp(P: PPropInfo; All: Boolean): Boolean;
  291.  
  292. begin
  293.   Result:=True;
  294. end;
  295.  
  296. procedure TBaseJWT.DoSaveToJSON(JSON: TJSONObject; All: Boolean);
  297.  
  298.  
  299. Var
  300.   D : TJSONEnum;
  301.   P : PPropinfo;
  302.   PL : PPropList;
  303.   I,VI,Count : Integer;
  304.   VF : Double;
  305.   C : Char;
  306.   CW : WideChar;
  307.   I64 : Int64;
  308.   W : UnicodeString;
  309.   S : String;
  310.  
  311. begin
  312.   Count:=GetPropList(Self,PL);
  313.   try
  314.     For I:=0 to Count-1 do
  315.       begin
  316.       P:=PL^[i];
  317.       if WriteProp(P,All) then
  318.         Case P^.PropType^.Kind of
  319.           tkInteger :
  320.             begin
  321.             VI:=GetOrdProp(Self,P);
  322.             if All or (VI<>0) then
  323.               JSON.Add(P^.Name,VI);
  324.             end;
  325.           tkChar :
  326.             begin
  327.             C:=Char(GetOrdProp(Self,P));
  328.             if All or (C<>#0) then
  329.               if C=#0 then
  330.                 JSON.Add(p^.Name,'')
  331.               else
  332.                 JSON.Add(p^.Name,C);
  333.             end;
  334.           tkEnumeration :
  335.             begin
  336.             vi:=GetOrdProp(Self,P);
  337.             JSON.Add(P^.Name,GetEnumName(p^.PropType,VI));
  338.             end;
  339.           tkFloat :
  340.             begin
  341.             VF:=GetFloatProp(Self,P);
  342.             If All or (VF<>0) then
  343.               JSON.Add(P^.Name,VF);
  344.             end;
  345.           tkSString,tkLString,tkAString :
  346.             begin
  347.             S:=GetStrProp(Self,P);
  348.             if All or (S<>'') then
  349.               JSON.Add(P^.Name,S);
  350.             end;
  351.           tkWChar:
  352.             begin
  353.             CW:=WideChar(GetOrdProp(Self,P));
  354.             if All or (CW<>#0) then
  355.               if CW=#0 then
  356.                 JSON.Add(p^.Name,'')
  357.               else
  358.                 JSON.Add(p^.Name,Utf8Encode(WideString(CW)));
  359.             end;
  360.           tkUString,tkWString,tkUChar:
  361.              begin
  362.               W:=GetWideStrProp(Self,P);
  363.               if All or (W<>'') then
  364.                 JSON.Add(P^.Name,Utf8Encode(W));
  365.               end;
  366.           tkBool :
  367.             JSON.Add(P^.Name,(GetOrdProp(Self,P)<>0));
  368.           tkInt64,tkQWord:
  369.             begin
  370.             I64:=GetInt64Prop(Self,P);
  371.             if All or (I64<>0) then
  372.               JSON.Add(p^.Name,I64);
  373.             end;
  374.           end;
  375.       end;
  376.   finally
  377.     FreeMem(PL);
  378.   end;
  379. end;
  380.  
  381. constructor TBaseJWT.Create;
  382. begin
  383.   Inherited Create;
  384. end;
  385.  
  386. procedure TBaseJWT.LoadFromJSON(JSON: TJSONObject);
  387. begin
  388.   DoLoadFromJSon(JSON);
  389. end;
  390.  
  391. procedure TBaseJWT.SaveToJSON(JSON: TJSONObject; All: Boolean);
  392. begin
  393.   DoSaveToJSon(JSON,All);
  394. end;
  395.  
  396. class function TBaseJWT.Base64ToBase64URL(AValue: string): string;
  397. begin
  398.   Result := StringsReplace(AValue, ['+', '/'], ['-', '_'], [rfReplaceAll]);
  399.   Result := TrimRightSet(Result, ['=']);
  400. end;
  401.  
  402. class function TBaseJWT.Base64URLToBase64(AValue: string): string;
  403. var
  404.   i,l: integer;
  405. begin
  406.   Result := StringsReplace(AValue, ['-', '_'], ['+', '/'], [rfReplaceAll]);
  407.   l := length(Result) mod 4;
  408.   if l > 0 then
  409.     Result:=Result+StringOfChar('=',4-l);
  410. end;
  411.  
  412. class function TBaseJWT.DecodeString(S: String): String;
  413. begin
  414.   Result:=DecodeStringBase64(Base64URLToBase64(S), True);
  415. end;
  416.  
  417. class function TBaseJWT.DecodeStringToJSON(S: String): TJSONObject;
  418.  
  419. Var
  420.   D : TJSONData;
  421. begin
  422.   D:=GetJSON(DecodeString(S));
  423.   if not (D is TJSONData) then
  424.     FreeAndNil(D);
  425.   Result:=TJSONObject(D);
  426. end;
  427.  
  428. end.
  429.  
  430.  


I'll be using the mentioned example as a base:

Code: Pascal  [Select][+][-]
  1.   { snip }
  2.  
  3.   // claims
  4.   jwt.Claims.exp:=DateTimeToUnix(Now+10);
  5.   jwt.Claims.iss:='FPC JWT';
  6.   with jwt.Claims as TMyClaims do
  7.     Name := 'Myself';
  8.  
  9.   { snip }
  10.  
  11.   // verify our generated token. This generates a new JWT
  12.   VerifyResult:=TMyJWT.ValidateJWT(aInput,Key);
  13.   if VerifyResult=Nil then
  14.     Raise Exception.Create('No verify resultn, verification failed!');
  15.  
  16.   with VerifyResult.Claims as TMyClaims do
  17.     Writeln(Name, ' ', admin);

You could also adjust your TJWT descendant like this so that you can directly use your TClaims descendant (again, based on the example):

Code: Pascal  [Select][+][-]
  1. { snip }
  2.   // A JWT that uses our claims
  3.   TMyJWT = Class(TJWT)
  4.   private
  5.     function GetClaims: TMyClaims;
  6.   protected
  7.     Function CreateClaims : TClaims; override;
  8.   public
  9.     property Claims: TMyClaims read GetClaims;
  10.   end;
  11.  
  12.  
  13. function TMyJWT.GetClaims: TMyClaims;
  14. begin
  15.   Result := TMyClaims(inherited Claims);
  16. end;
  17.  
  18. function TMyJWT.CreateClaims: TClaims;
  19. begin
  20.   Result:=TMyClaims.Create;
  21. end;
  22.  
  23. { snip }
  24.  
  25. var
  26.   aInput, aPrivateKeyPEM, aPublicKeyPEM: String;
  27.   Key: TJWTKey;
  28.   JWT : TJWT;
  29.   VerifyResult: TMyJWT; { <=== !!!! }
  30.   Signer: TJWTSigner;
  31.   NewDER: TBytes;
  32.   RSAPublic: TX509RSAPublicKey;
  33.   RSAPrivate: TX509RSAPrivateKey;
  34.  
  35. { snip }
  36.  
  37.   // verify our generated token. This generates a new JWT
  38.   VerifyResult:=TMyJWT.ValidateJWT(aInput,Key) as TMyJWT;
  39.   if VerifyResult=Nil then
  40.     Raise Exception.Create('No verify resultn, verification failed!');
  41.   Writeln(VerifyResult.Claims.Name, ' ', VerifyResult.Claims.admin);
Lazarus 2.2.2 FPC 3.2.2 Windows (qt5) Anchor Docking

PascalDragon

  • Hero Member
  • *****
  • Posts: 4552
  • Compiler Developer
Re: Is possible to add custom claims to TJWT?
« Reply #8 on: August 26, 2022, 08:59:13 am »
The method "ValidateJWT" does not exsist on fpjwt unit.

This seems to be a recent addition so you'll have to copy the fpjwt unit from main.

LeoBruno

  • Jr. Member
  • **
  • Posts: 61
Re: Is possible to add custom claims to TJWT?
« Reply #9 on: August 27, 2022, 12:26:24 am »
I want to thank you very much for your help.

Actually, I figured out:

To get the claims, all that is needed, is to call TJWT.Jose.DecodeString, passing only the payload portion of the token (the middle part).

Code: Pascal  [Select][+][-]
  1.  
  2. procedure TForm1.Button2Click(Sender: TObject);
  3. var
  4.   LJWT: TJWT;
  5.   _Token,
  6.   _PayLoad: string;
  7. begin
  8.  LJWT := TJWT.Create;
  9.  try
  10.    LJWT.JOSE.alg := 'HS256';
  11.    LJWT.JOSE.typ := 'JWT';
  12.  
  13.    _Token := mJWT.Text; //   mJWT is a memo
  14.    _PayLoad :=  _Token.Split('.')[1]; //get the middle portion of the token
  15.  
  16.    Memo1.Lines.Add(LJWT.JOSE.DecodeString(_PayLoad));
  17.    //the above line give me the claims on a Json Object as follows:
  18.    // { "iss" : "John Doe asadsadsad", "sub" : "1234567890", "iat" : 1516239022 }
  19.  finally
  20.    LJWT.Free;
  21.  end;
  22.  
  23.  

I'm using external libs to generate the token.
It seems that when "fpjwt.pp" from main becomes avaiable, It won't be necessary anymore.

Thank's again for your time, patience and support!


The method "ValidateJWT" does not exsist on fpjwt unit.

This seems to be a recent addition so you'll have to copy the fpjwt unit from main.
[/code]
« Last Edit: August 30, 2022, 10:51:20 pm by LeoBruno »
Lazarus 2.2.2 FPC 3.2.2 Windows (qt5) Anchor Docking

PascalDragon

  • Hero Member
  • *****
  • Posts: 4552
  • Compiler Developer
Re: Is possible to add custom claims to TJWT?
« Reply #10 on: August 28, 2022, 06:11:37 pm »
To get the claims, all that is needed, is to call TJWT.Jose.DecodeString, passing only the payload portion of the token (the middle part).

Please note that in that case you won't know whether someone manipulated the token (which is why the method is called ValidateJWT after all).

LeoBruno

  • Jr. Member
  • **
  • Posts: 61
Re: Is possible to add custom claims to TJWT?
« Reply #11 on: August 30, 2022, 10:40:05 pm »
Thank you for the Heads UP.

I use HashLoad's Horse (https://github.com/HashLoad/horse).

A middleware that can be used in order to verify the Token is avaiable (https://github.com/HashLoad/horse-jwt).


To get the claims, all that is needed, is to call TJWT.Jose.DecodeString, passing only the payload portion of the token (the middle part).

Please note that in that case you won't know whether someone manipulated the token (which is why the method is called ValidateJWT after all).
« Last Edit: August 30, 2022, 11:05:09 pm by LeoBruno »
Lazarus 2.2.2 FPC 3.2.2 Windows (qt5) Anchor Docking

PascalDragon

  • Hero Member
  • *****
  • Posts: 4552
  • Compiler Developer
Re: Is possible to add custom claims to TJWT?
« Reply #12 on: August 31, 2022, 08:56:12 am »
Thank you for the Heads UP.

I use HashLoad's Horse (https://github.com/HashLoad/horse).

A middleware that can be used in order to verify the Token is avaiable (https://github.com/HashLoad/horse-jwt).

It seems that code does indeed do validation. So as long as you rely on what the callback function returned by the HorseJWT function of the Horse.JWT unit returns you should be fine. You might want to check what happens if you pass an invalid token (e.g. modify it in the browser's development tools) so that your own code aborts as well.

 

TinyPortal © 2005-2018