mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 15:55:54 +01:00
ADDED JWT Checks for: NotBefore, ExpirationTime, IssuedAt
Updated JWT sample Added more unit tests
This commit is contained in:
parent
214d378cdc
commit
e1225fdcbe
@ -1,8 +1,8 @@
|
|||||||
object Form7: TForm7
|
object Form7: TForm7
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 0
|
Top = 0
|
||||||
Caption = 'Form7'
|
Caption = 'JWT Sample'
|
||||||
ClientHeight = 231
|
ClientHeight = 160
|
||||||
ClientWidth = 505
|
ClientWidth = 505
|
||||||
Color = clBtnFace
|
Color = clBtnFace
|
||||||
Font.Charset = DEFAULT_CHARSET
|
Font.Charset = DEFAULT_CHARSET
|
||||||
|
@ -44,6 +44,8 @@ begin
|
|||||||
finally
|
finally
|
||||||
lClt.Free;
|
lClt.Free;
|
||||||
end;
|
end;
|
||||||
|
ShowMessage
|
||||||
|
('In the next 15 seconds you can request protected resources. After your token will expires!');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm7.Button2Click(Sender: TObject);
|
procedure TForm7.Button2Click(Sender: TObject);
|
||||||
@ -55,7 +57,8 @@ begin
|
|||||||
try
|
try
|
||||||
lClt.Header('Authentication', 'bearer ' + FToken);
|
lClt.Header('Authentication', 'bearer ' + FToken);
|
||||||
lResp := lClt.doGET('/', []);
|
lResp := lClt.doGET('/', []);
|
||||||
ShowMessage(lResp.BodyAsJSONValue.Value);
|
ShowMessage(lResp.ResponseText + sLineBreak +
|
||||||
|
lResp.BodyAsJSONValue.Value);
|
||||||
finally
|
finally
|
||||||
lClt.Free;
|
lClt.Free;
|
||||||
end;
|
end;
|
||||||
|
@ -46,8 +46,9 @@ procedure TMyController.OnBeforeAction(Context: TWebContext; const AActionName:
|
|||||||
var Handled: Boolean);
|
var Handled: Boolean);
|
||||||
var
|
var
|
||||||
lJWT: TJWT;
|
lJWT: TJWT;
|
||||||
lAuthHeader: string;
|
lAuthHeader: String;
|
||||||
lToken: string;
|
lToken: String;
|
||||||
|
lError: String;
|
||||||
begin
|
begin
|
||||||
{ Executed before each action
|
{ Executed before each action
|
||||||
if handled is true (or an exception is raised) the actual
|
if handled is true (or an exception is raised) the actual
|
||||||
@ -59,8 +60,9 @@ begin
|
|||||||
lJWT := TJWT.Create('mysecret');
|
lJWT := TJWT.Create('mysecret');
|
||||||
try
|
try
|
||||||
lJWT.Claims.Issuer := 'dmvcframework app';
|
lJWT.Claims.Issuer := 'dmvcframework app';
|
||||||
lJWT.Claims.ExpirationTime := Now + OneHour;
|
lJWT.Claims.ExpirationTime := Now + OneSecond * 15;
|
||||||
lJWT.Claims.NotBefore := Now - OneMinute * 5;
|
lJWT.Claims.NotBefore := Now - OneMinute * 5;
|
||||||
|
lJWT.Claims.IssuedAt := Now - OneMinute * 5;
|
||||||
lJWT.CustomClaims['username'] := 'dteti';
|
lJWT.CustomClaims['username'] := 'dteti';
|
||||||
Render(lJWT.GetToken);
|
Render(lJWT.GetToken);
|
||||||
Exit;
|
Exit;
|
||||||
@ -69,8 +71,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// JWT checking
|
||||||
lJWT := TJWT.Create('mysecret');
|
lJWT := TJWT.Create('mysecret');
|
||||||
try
|
try
|
||||||
|
// in this demo, no tolerance... so no LEEWAY time
|
||||||
|
lJWT.LeewaySeconds := 0;
|
||||||
|
|
||||||
lAuthHeader := Context.Request.Headers['Authentication'];
|
lAuthHeader := Context.Request.Headers['Authentication'];
|
||||||
if lAuthHeader.IsEmpty then
|
if lAuthHeader.IsEmpty then
|
||||||
begin
|
begin
|
||||||
@ -84,7 +90,7 @@ begin
|
|||||||
lToken := lAuthHeader.Remove(0, 'bearer'.Length).Trim;
|
lToken := lAuthHeader.Remove(0, 'bearer'.Length).Trim;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if not lJWT.IsValidToken(lToken) then
|
if not lJWT.IsValidToken(lToken, lError) then
|
||||||
begin
|
begin
|
||||||
Render(http_status.Unauthorized, 'Invalid Token, Authentication Required');
|
Render(http_status.Unauthorized, 'Invalid Token, Authentication Required');
|
||||||
Handled := True;
|
Handled := True;
|
||||||
|
@ -3,9 +3,13 @@ unit MVCFramework.JWT;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.Generics.Collections;
|
System.Generics.Collections, System.JSON;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
{$SCOPEDENUMS ON}
|
||||||
|
TJWTCheckableClaim = (ExpirationTime, NotBefore, IssuedAt);
|
||||||
|
TJWTCheckableClaims = set of TJWTCheckableClaim;
|
||||||
|
|
||||||
TJWTRegisteredClaimNames = class sealed
|
TJWTRegisteredClaimNames = class sealed
|
||||||
public
|
public
|
||||||
const
|
const
|
||||||
@ -152,22 +156,31 @@ type
|
|||||||
FRegisteredClaims: TJWTRegisteredClaims;
|
FRegisteredClaims: TJWTRegisteredClaims;
|
||||||
FCustomClaims: TJWTCustomClaims;
|
FCustomClaims: TJWTCustomClaims;
|
||||||
FHMACAlgorithm: String;
|
FHMACAlgorithm: String;
|
||||||
|
FLeewaySeconds: Int64;
|
||||||
|
FRegClaimsToChecks: TJWTCheckableClaims;
|
||||||
procedure SetHMACAlgorithm(const Value: String);
|
procedure SetHMACAlgorithm(const Value: String);
|
||||||
|
procedure SetLeewaySeconds(const Value: Int64);
|
||||||
|
procedure SetChecks(const Value: TJWTCheckableClaims);
|
||||||
|
function CheckExpirationTime(Payload: TJSONObject; out Error: String): Boolean;
|
||||||
|
function CheckNotBefore(Payload: TJSONObject; out Error: String): Boolean;
|
||||||
|
function CheckIssuedAt(Payload: TJSONObject; out Error: String): Boolean;
|
||||||
public
|
public
|
||||||
constructor Create(const SecretKey: String); virtual;
|
constructor Create(const SecretKey: String); virtual;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
function GetToken: String;
|
function GetToken: String;
|
||||||
function IsValidToken(const Token: String): Boolean;
|
function IsValidToken(const Token: String; out Error: String): Boolean;
|
||||||
procedure LoadToken(const Token: String);
|
procedure LoadToken(const Token: String);
|
||||||
property Claims: TJWTRegisteredClaims read FRegisteredClaims;
|
property Claims: TJWTRegisteredClaims read FRegisteredClaims;
|
||||||
property CustomClaims: TJWTCustomClaims read FCustomClaims;
|
property CustomClaims: TJWTCustomClaims read FCustomClaims;
|
||||||
property HMACAlgorithm: String read FHMACAlgorithm write SetHMACAlgorithm;
|
property HMACAlgorithm: String read FHMACAlgorithm write SetHMACAlgorithm;
|
||||||
|
property LeewaySeconds: Int64 read FLeewaySeconds write SetLeewaySeconds;
|
||||||
|
property RegClaimsToChecks: TJWTCheckableClaims read FRegClaimsToChecks write SetChecks;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.SysUtils, System.JSON, MVCFramework.Commons, MVCFramework.HMAC, System.DateUtils;
|
System.SysUtils, MVCFramework.Commons, MVCFramework.HMAC, System.DateUtils;
|
||||||
|
|
||||||
{ TJWTRegisteredClaims }
|
{ TJWTRegisteredClaims }
|
||||||
|
|
||||||
@ -293,6 +306,94 @@ end;
|
|||||||
|
|
||||||
{ TJWT }
|
{ TJWT }
|
||||||
|
|
||||||
|
function TJWT.CheckExpirationTime(Payload: TJSONObject;
|
||||||
|
out Error: String): Boolean;
|
||||||
|
var
|
||||||
|
lJValue: TJSONValue;
|
||||||
|
lIntValue: Int64;
|
||||||
|
lValue: string;
|
||||||
|
begin
|
||||||
|
lJValue := Payload.GetValue(TJWTRegisteredClaimNames.ExpirationTime);
|
||||||
|
if not Assigned(lJValue) then
|
||||||
|
begin
|
||||||
|
Error := TJWTRegisteredClaimNames.ExpirationTime + ' not set';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
lValue := lJValue.Value;
|
||||||
|
if not TryStrToInt64(lValue, lIntValue) then
|
||||||
|
begin
|
||||||
|
Error := TJWTRegisteredClaimNames.ExpirationTime + ' is not an integer';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if UnixToDateTime(lIntValue) <= Now - FLeewaySeconds * OneSecond then
|
||||||
|
begin
|
||||||
|
Error := 'Token expired';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJWT.CheckIssuedAt(Payload: TJSONObject; out Error: String): Boolean;
|
||||||
|
var
|
||||||
|
lJValue: TJSONValue;
|
||||||
|
lIntValue: Int64;
|
||||||
|
lValue: string;
|
||||||
|
begin
|
||||||
|
lJValue := Payload.GetValue(TJWTRegisteredClaimNames.IssuedAt);
|
||||||
|
if not Assigned(lJValue) then
|
||||||
|
begin
|
||||||
|
Error := TJWTRegisteredClaimNames.IssuedAt + ' not set';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
lValue := lJValue.Value;
|
||||||
|
if not TryStrToInt64(lValue, lIntValue) then
|
||||||
|
begin
|
||||||
|
Error := TJWTRegisteredClaimNames.IssuedAt + ' is not an integer';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if UnixToDateTime(lIntValue) >= Now + FLeewaySeconds * OneSecond then
|
||||||
|
begin
|
||||||
|
Error := 'Token is issued in the future';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJWT.CheckNotBefore(Payload: TJSONObject; out Error: String): Boolean;
|
||||||
|
var
|
||||||
|
lJValue: TJSONValue;
|
||||||
|
lIntValue: Int64;
|
||||||
|
lValue: string;
|
||||||
|
begin
|
||||||
|
lJValue := Payload.GetValue(TJWTRegisteredClaimNames.NotBefore);
|
||||||
|
if not Assigned(lJValue) then
|
||||||
|
begin
|
||||||
|
Error := TJWTRegisteredClaimNames.NotBefore + ' not set';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
lValue := lJValue.Value;
|
||||||
|
if not TryStrToInt64(lValue, lIntValue) then
|
||||||
|
begin
|
||||||
|
Error := TJWTRegisteredClaimNames.NotBefore + ' is not an integer';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if UnixToDateTime(lIntValue) >= Now + FLeewaySeconds * OneSecond then
|
||||||
|
begin
|
||||||
|
Error := 'Token still not valid';
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
constructor TJWT.Create(const SecretKey: String);
|
constructor TJWT.Create(const SecretKey: String);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
@ -300,6 +401,9 @@ begin
|
|||||||
FRegisteredClaims := TJWTRegisteredClaims.Create;
|
FRegisteredClaims := TJWTRegisteredClaims.Create;
|
||||||
FCustomClaims := TJWTCustomClaims.Create;
|
FCustomClaims := TJWTCustomClaims.Create;
|
||||||
FHMACAlgorithm := 'HS256';
|
FHMACAlgorithm := 'HS256';
|
||||||
|
FLeewaySeconds := 300; // 5 minutes of leeway
|
||||||
|
FRegClaimsToChecks := [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore,
|
||||||
|
TJWTCheckableClaim.IssuedAt];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TJWT.Destroy;
|
destructor TJWT.Destroy;
|
||||||
@ -325,7 +429,15 @@ begin
|
|||||||
for lRegClaimName in TJWTRegisteredClaimNames.Names do
|
for lRegClaimName in TJWTRegisteredClaimNames.Names do
|
||||||
begin
|
begin
|
||||||
if FRegisteredClaims.Contains(lRegClaimName) then
|
if FRegisteredClaims.Contains(lRegClaimName) then
|
||||||
lPayload.AddPair(lRegClaimName, FRegisteredClaims[lRegClaimName]);
|
begin
|
||||||
|
if (lRegClaimName = TJWTRegisteredClaimNames.ExpirationTime) or
|
||||||
|
(lRegClaimName = TJWTRegisteredClaimNames.NotBefore) or
|
||||||
|
(lRegClaimName = TJWTRegisteredClaimNames.IssuedAt) then
|
||||||
|
lPayload.AddPair(lRegClaimName,
|
||||||
|
TJSONNumber.Create(StrToInt64(FRegisteredClaims[lRegClaimName])))
|
||||||
|
else
|
||||||
|
lPayload.AddPair(lRegClaimName, FRegisteredClaims[lRegClaimName]);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
for lCustomClaimName in FCustomClaims.Keys do
|
for lCustomClaimName in FCustomClaims.Keys do
|
||||||
@ -347,35 +459,61 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TJWT.IsValidToken(const Token: String): Boolean;
|
function TJWT.IsValidToken(const Token: String; out Error: String): Boolean;
|
||||||
var
|
var
|
||||||
lPieces: TArray<String>;
|
lPieces: TArray<String>;
|
||||||
lJHeader: TJSONObject;
|
lJHeader: TJSONObject;
|
||||||
lJAlg: TJSONString;
|
lJAlg: TJSONString;
|
||||||
lAlgName: string;
|
lAlgName: string;
|
||||||
lJPayload: TJSONObject;
|
lJPayload: TJSONObject;
|
||||||
|
lError: String;
|
||||||
begin
|
begin
|
||||||
lPieces := Token.Split(['.']);
|
lPieces := Token.Split(['.']);
|
||||||
if Length(lPieces) <> 3 then
|
if Length(lPieces) <> 3 then
|
||||||
Exit(False);
|
Exit(false);
|
||||||
lJHeader := TJSONObject.ParseJSONValue(B64Decode(lPieces[0])) as TJSONObject;
|
lJHeader := TJSONObject.ParseJSONValue(B64Decode(lPieces[0])) as TJSONObject;
|
||||||
try
|
try
|
||||||
if not Assigned(lJHeader) then
|
if not Assigned(lJHeader) then
|
||||||
Exit(False);
|
Exit(false);
|
||||||
|
|
||||||
lJPayload := TJSONObject.ParseJSONValue(B64Decode(lPieces[1])) as TJSONObject;
|
lJPayload := TJSONObject.ParseJSONValue(B64Decode(lPieces[1])) as TJSONObject;
|
||||||
try
|
try
|
||||||
if not Assigned(lJPayload) then
|
if not Assigned(lJPayload) then
|
||||||
Exit(False);
|
Exit(false);
|
||||||
|
|
||||||
if not lJHeader.TryGetValue<TJSONString>('alg', lJAlg) then
|
if not lJHeader.TryGetValue<TJSONString>('alg', lJAlg) then
|
||||||
Exit(False);
|
Exit(false);
|
||||||
|
|
||||||
lAlgName := lJAlg.Value;
|
lAlgName := lJAlg.Value;
|
||||||
Result := Token = lPieces[0] + '.' + lPieces[1] + '.' +
|
Result := Token = lPieces[0] + '.' + lPieces[1] + '.' +
|
||||||
B64Encode(
|
B64Encode(
|
||||||
HMAC(lAlgName, lPieces[0] + '.' + lPieces[1], FSecretKey)
|
HMAC(lAlgName, lPieces[0] + '.' + lPieces[1], FSecretKey)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// if the token is correctly signed and has not been tampered,
|
||||||
|
// let's check it's validity usinf nbf, exp, iat as configured in
|
||||||
|
// the RegClaimsToCheck property
|
||||||
|
if Result then
|
||||||
|
begin
|
||||||
|
if TJWTCheckableClaim.ExpirationTime in RegClaimsToChecks then
|
||||||
|
begin
|
||||||
|
if not CheckExpirationTime(lJPayload, lError) then
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if TJWTCheckableClaim.NotBefore in RegClaimsToChecks then
|
||||||
|
begin
|
||||||
|
if not CheckNotBefore(lJPayload, lError) then
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if TJWTCheckableClaim.IssuedAt in RegClaimsToChecks then
|
||||||
|
begin
|
||||||
|
if not CheckIssuedAt(lJPayload, lError) then
|
||||||
|
Exit(false);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
finally
|
finally
|
||||||
lJPayload.Free;
|
lJPayload.Free;
|
||||||
end;
|
end;
|
||||||
@ -395,9 +533,10 @@ var
|
|||||||
j: Integer;
|
j: Integer;
|
||||||
lIsRegistered: Boolean;
|
lIsRegistered: Boolean;
|
||||||
lValue: string;
|
lValue: string;
|
||||||
|
lError: String;
|
||||||
begin
|
begin
|
||||||
if not IsValidToken(Token) then
|
if not IsValidToken(Token, lError) then
|
||||||
raise EMVCJWTException.Create('Invalid token');
|
raise EMVCJWTException.Create('Invalid token: ' + lError);
|
||||||
|
|
||||||
lPieces := Token.Split(['.']);
|
lPieces := Token.Split(['.']);
|
||||||
lJHeader := TJSONObject.ParseJSONValue(B64Decode(lPieces[0])) as TJSONObject;
|
lJHeader := TJSONObject.ParseJSONValue(B64Decode(lPieces[0])) as TJSONObject;
|
||||||
@ -413,7 +552,7 @@ begin
|
|||||||
FCustomClaims.FClaims.Clear;
|
FCustomClaims.FClaims.Clear;
|
||||||
for i := 0 to lJPayload.Count - 1 do
|
for i := 0 to lJPayload.Count - 1 do
|
||||||
begin
|
begin
|
||||||
lIsRegistered := False;
|
lIsRegistered := false;
|
||||||
lJPair := lJPayload.Pairs[i];
|
lJPair := lJPayload.Pairs[i];
|
||||||
lName := lJPair.JsonString.Value;
|
lName := lJPair.JsonString.Value;
|
||||||
lValue := lJPair.JsonValue.Value;
|
lValue := lJPair.JsonValue.Value;
|
||||||
@ -440,9 +579,19 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TJWT.SetChecks(const Value: TJWTCheckableClaims);
|
||||||
|
begin
|
||||||
|
FRegClaimsToChecks := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TJWT.SetHMACAlgorithm(const Value: String);
|
procedure TJWT.SetHMACAlgorithm(const Value: String);
|
||||||
begin
|
begin
|
||||||
FHMACAlgorithm := Value;
|
FHMACAlgorithm := Value;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TJWT.SetLeewaySeconds(const Value: Int64);
|
||||||
|
begin
|
||||||
|
FLeewaySeconds := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -71,6 +71,10 @@ type
|
|||||||
procedure TestStorage;
|
procedure TestStorage;
|
||||||
procedure TestCreateAndValidateToken;
|
procedure TestCreateAndValidateToken;
|
||||||
procedure TestLoadToken;
|
procedure TestLoadToken;
|
||||||
|
procedure TestNotBefore;
|
||||||
|
procedure TestExpirationTime;
|
||||||
|
procedure TestIssuedAt;
|
||||||
|
procedure TestDefaults;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@ -1078,15 +1082,52 @@ end;
|
|||||||
procedure TTestJWT.TestCreateAndValidateToken;
|
procedure TTestJWT.TestCreateAndValidateToken;
|
||||||
var
|
var
|
||||||
lToken: string;
|
lToken: string;
|
||||||
|
lError: string;
|
||||||
begin
|
begin
|
||||||
FJWT.Claims.Issuer := 'bit Time Professionals';
|
FJWT.Claims.Issuer := 'bit Time Professionals';
|
||||||
FJWT.Claims.Subject := 'DelphiMVCFramework';
|
FJWT.Claims.Subject := 'DelphiMVCFramework';
|
||||||
FJWT.Claims.JWT_ID := TGUID.NewGuid.ToString;
|
FJWT.Claims.JWT_ID := TGUID.NewGuid.ToString;
|
||||||
FJWT.CustomClaims['username'] := 'dteti';
|
FJWT.CustomClaims['username'] := 'dteti';
|
||||||
FJWT.CustomClaims['userrole'] := 'admin';
|
FJWT.CustomClaims['userrole'] := 'admin';
|
||||||
|
FJWT.Claims.ExpirationTime := Tomorrow;
|
||||||
|
FJWT.Claims.IssuedAt := Yesterday;
|
||||||
|
FJWT.Claims.NotBefore := Yesterday;
|
||||||
lToken := FJWT.GetToken;
|
lToken := FJWT.GetToken;
|
||||||
// TFile.WriteAllText('jwt_token.dat', lToken);
|
// TFile.WriteAllText('jwt_token.dat', lToken);
|
||||||
CheckTrue(FJWT.IsValidToken(lToken), 'Generated token is not valid');
|
CheckTrue(FJWT.IsValidToken(lToken, lError), 'Generated token is not valid');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTestJWT.TestDefaults;
|
||||||
|
begin
|
||||||
|
CheckEquals('HS256', FJWT.HMACAlgorithm, 'Default algorithm should be HS256');
|
||||||
|
CheckEquals(300, FJWT.LeewaySeconds, 'Default leeway should be 5 minutes');
|
||||||
|
if FJWT.RegClaimsToChecks * [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore,
|
||||||
|
TJWTCheckableClaim.IssuedAt] <> [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore,
|
||||||
|
TJWTCheckableClaim.IssuedAt] then
|
||||||
|
Fail('Default RegClaimsToCheck not correct');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTestJWT.TestExpirationTime;
|
||||||
|
var
|
||||||
|
lToken: string;
|
||||||
|
lError: string;
|
||||||
|
begin
|
||||||
|
FJWT.RegClaimsToChecks := [TJWTCheckableClaim.ExpirationTime];
|
||||||
|
FJWT.Claims.ExpirationTime := Tomorrow;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckTrue(FJWT.IsValidToken(lToken, lError), 'Valid token is considered expired');
|
||||||
|
|
||||||
|
FJWT.Claims.ExpirationTime := Yesterday;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckFalse(FJWT.IsValidToken(lToken, lError), 'Expired token is considered valid');
|
||||||
|
|
||||||
|
FJWT.Claims.ExpirationTime := Now;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckTrue(FJWT.IsValidToken(lToken, lError), 'Valid token is considered expired');
|
||||||
|
|
||||||
|
FJWT.Claims.ExpirationTime := Now - (FJWT.LeewaySeconds + 1) * OneSecond;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckFalse(FJWT.IsValidToken(lToken, lError), 'Expired token is considered valid');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TTestJWT.TestHMAC;
|
procedure TTestJWT.TestHMAC;
|
||||||
@ -1104,6 +1145,29 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TTestJWT.TestIssuedAt;
|
||||||
|
var
|
||||||
|
lToken: string;
|
||||||
|
lError: string;
|
||||||
|
begin
|
||||||
|
FJWT.RegClaimsToChecks := [TJWTCheckableClaim.IssuedAt];
|
||||||
|
FJWT.Claims.IssuedAt := Yesterday;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckTrue(FJWT.IsValidToken(lToken, lError), 'Valid token is considered not valid');
|
||||||
|
|
||||||
|
FJWT.Claims.IssuedAt := Tomorrow;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckFalse(FJWT.IsValidToken(lToken, lError), 'Still-not-valid token is considered valid');
|
||||||
|
|
||||||
|
FJWT.Claims.IssuedAt := Now;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckTrue(FJWT.IsValidToken(lToken, lError), 'Valid token is considered not valid');
|
||||||
|
|
||||||
|
FJWT.Claims.IssuedAt := Now + (FJWT.LeewaySeconds + 1) * OneSecond;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckFalse(FJWT.IsValidToken(lToken, lError), 'Still-not-valid token is considered valid');
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TTestJWT.TestLoadToken;
|
procedure TTestJWT.TestLoadToken;
|
||||||
var
|
var
|
||||||
lToken: string;
|
lToken: string;
|
||||||
@ -1113,7 +1177,7 @@ begin
|
|||||||
FJWT.Claims.Subject := 'DelphiMVCFramework';
|
FJWT.Claims.Subject := 'DelphiMVCFramework';
|
||||||
FJWT.Claims.Audience := 'DelphiDevelopers';
|
FJWT.Claims.Audience := 'DelphiDevelopers';
|
||||||
FJWT.Claims.IssuedAt := EncodeDateTime(2011, 11, 17, 17, 30, 0, 0);
|
FJWT.Claims.IssuedAt := EncodeDateTime(2011, 11, 17, 17, 30, 0, 0);
|
||||||
FJWT.Claims.ExpirationTime := FJWT.Claims.IssuedAt + OneHour * 2;
|
FJWT.Claims.ExpirationTime := Now + OneHour * 2;
|
||||||
FJWT.Claims.NotBefore := EncodeDateTime(2011, 11, 17, 17, 30, 0, 0);
|
FJWT.Claims.NotBefore := EncodeDateTime(2011, 11, 17, 17, 30, 0, 0);
|
||||||
FJWT.Claims.JWT_ID := '123456';
|
FJWT.Claims.JWT_ID := '123456';
|
||||||
FJWT.CustomClaims['username'] := 'dteti';
|
FJWT.CustomClaims['username'] := 'dteti';
|
||||||
@ -1141,6 +1205,29 @@ begin
|
|||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TTestJWT.TestNotBefore;
|
||||||
|
var
|
||||||
|
lToken: string;
|
||||||
|
lError: string;
|
||||||
|
begin
|
||||||
|
FJWT.RegClaimsToChecks := [TJWTCheckableClaim.NotBefore];
|
||||||
|
FJWT.Claims.NotBefore := Yesterday;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckTrue(FJWT.IsValidToken(lToken, lError), 'Valid token is considered not valid');
|
||||||
|
|
||||||
|
FJWT.Claims.NotBefore := Tomorrow;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckFalse(FJWT.IsValidToken(lToken, lError), 'Still-not-valid token is considered valid');
|
||||||
|
|
||||||
|
FJWT.Claims.NotBefore := Now;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckTrue(FJWT.IsValidToken(lToken, lError), 'Valid token is considered not valid');
|
||||||
|
|
||||||
|
FJWT.Claims.NotBefore := Now + (FJWT.LeewaySeconds + 1) * OneSecond;
|
||||||
|
lToken := FJWT.GetToken;
|
||||||
|
CheckFalse(FJWT.IsValidToken(lToken, lError), 'Still-not-valid token is considered valid');
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TTestJWT.TestStorage;
|
procedure TTestJWT.TestStorage;
|
||||||
begin
|
begin
|
||||||
FJWT.Claims.Issuer := 'bit Time Professionals';
|
FJWT.Claims.Issuer := 'bit Time Professionals';
|
||||||
|
Loading…
Reference in New Issue
Block a user