delphimvcframework/sources/MVCFramework.JWT.pas

674 lines
21 KiB
ObjectPascal
Raw Normal View History

// ***************************************************************************
//
// Delphi MVC Framework
//
2018-01-29 17:30:53 +01:00
// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// *************************************************************************** }
2016-05-18 18:21:43 +02:00
unit MVCFramework.JWT;
{$I dmvcframework.inc}
interface
2016-05-18 18:21:43 +02:00
uses
System.Generics.Collections,
System.JSON,
MVCFramework,
MVCFramework.Patches;
2016-05-18 18:21:43 +02:00
type
{$SCOPEDENUMS ON}
TJWTCheckableClaim = (ExpirationTime, NotBefore, IssuedAt);
TJWTCheckableClaims = set of TJWTCheckableClaim;
2016-05-18 18:21:43 +02:00
TJWTRegisteredClaimNames = class sealed
public
const
Issuer: string = 'iss';
Subject: string = 'sub';
Audience: string = 'aud';
ExpirationTime: string = 'exp';
NotBefore: string = 'nbf';
IssuedAt: string = 'iat';
JWT_ID: string = 'jti';
2016-05-18 18:21:43 +02:00
Names: array [0 .. 6] of string = (
'iss',
'sub',
'aud',
'exp',
'nbf',
'iat',
'jti');
end;
TJWTDictionaryObject = class
private
FClaims: TDictionary<string, string>;
function GetItem(const Index: string): string;
procedure SetItem(const Index, Value: string);
function GetItemAsDateTime(const Index: string): TDateTime;
procedure SetItemAsDateTime(const Index: string; const Value: TDateTime);
property ItemsAsDateTime[const index: string]: TDateTime read GetItemAsDateTime write SetItemAsDateTime;
property Items[const index: string]: string read GetItem write SetItem; default;
2016-05-18 18:21:43 +02:00
protected
function Contains(const Index: string): Boolean;
function Keys: TArray<string>;
2016-05-18 18:21:43 +02:00
public
constructor Create; virtual;
destructor Destroy; override;
end;
/// <summary>
/// https://tools.ietf.org/html/rfc7519#section-4.1.1
/// </summary>
TJWTRegisteredClaims = class(TJWTDictionaryObject)
private
procedure SetAudience(const Value: string);
2016-05-18 18:21:43 +02:00
procedure SetExpirationTime(const Value: TDateTime);
procedure SetIssuedAt(const Value: TDateTime);
procedure SetISSUER(const Value: string);
procedure SetJWT_ID(const Value: string);
2016-05-18 18:21:43 +02:00
procedure SetNotBefore(const Value: TDateTime);
procedure SetSubject(const Value: string);
function GetAudience: string;
2016-05-18 18:21:43 +02:00
function GetExpirationTime: TDateTime;
function GetIssuedAt: TDateTime;
function GetJWT_ID: string;
2016-05-18 18:21:43 +02:00
function GetNotBefore: TDateTime;
function GetSubject: string;
function GetIssuer: string;
2016-05-18 18:21:43 +02:00
public
/// <summary>
/// "iss" (Issuer) Claim
/// The " iss "(issuer) claim identifies The principal that issued The
/// JWT. The processing of this claim is generally application specific.
/// The " iss " value is a case-sensitive string containing a StringOrURI
/// value.Use of this claim is OPTIONAL.
/// </summary>
property Issuer: string read GetIssuer write SetISSUER;
2016-05-18 18:21:43 +02:00
/// <summary>
/// "sub" (Subject) Claim
/// The "sub" (subject) claim identifies the principal that is the
/// subject of the JWT. The claims in a JWT are normally statements
/// about the subject. The subject value MUST either be scoped to be
/// locally unique in the context of the issuer or be globally unique.
/// The processing of this claim is generally application specific. The
/// "sub" value is a case-sensitive string containing a StringOrURI
/// value. Use of this claim is OPTIONAL.
/// </summary>
property Subject: string read GetSubject write SetSubject;
2016-05-18 18:21:43 +02:00
/// <summary>
/// "aud" (Audience) Claim
/// The "aud" (audience) claim identifies the recipients that the JWT is
/// intended for. Each principal intended to process the JWT MUST
/// identify itself with a value in the audience claim. If the principal
/// processing the claim does not identify itself with a value in the
/// "aud" claim when this claim is present, then the JWT MUST be
/// rejected. In the general case, the "aud" value is an array of case-
/// sensitive strings, each containing a StringOrURI value. In the
/// special case when the JWT has one audience, the "aud" value MAY be a
/// single case-sensitive string containing a StringOrURI value. The
/// interpretation of audience values is generally application specific.
/// Use of this claim is OPTIONAL.
/// </summary>
property Audience: string read GetAudience write SetAudience;
2016-05-18 18:21:43 +02:00
/// <summary>
/// "exp" (Expiration Time) Claim
/// The "exp" (expiration time) claim identifies the expiration time on
/// or after which the JWT MUST NOT be accepted for processing. The
/// processing of the "exp" claim requires that the current date/time
/// MUST be before the expiration date/time listed in the "exp" claim.
/// Implementers MAY provide for some small leeway, usually no more than
/// a few minutes, to account for clock skew. Its value MUST be a number
/// containing a NumericDate value. Use of this claim is OPTIONAL.
/// </summary>
property ExpirationTime: TDateTime read GetExpirationTime write SetExpirationTime;
/// <summary>
/// "nbf" (Not Before) Claim
/// The "nbf" (not before) claim identifies the time before which the JWT
/// MUST NOT be accepted for processing. The processing of the "nbf"
/// claim requires that the current date/time MUST be after or equal to
/// the not-before date/time listed in the "nbf" claim. Implementers MAY
/// provide for some small leeway, usually no more than a few minutes, to
/// account for clock skew. Its value MUST be a number containing a
/// NumericDate value. Use of this claim is OPTIONAL.
/// </summary>
property NotBefore: TDateTime read GetNotBefore write SetNotBefore;
/// <summary>
/// "iat" (Issued At) Claim
/// The "iat" (issued at) claim identifies the time at which the JWT was
/// issued. This claim can be used to determine the age of the JWT. Its
/// value MUST be a number containing a NumericDate value. Use of this
/// claim is OPTIONAL.
/// </summary>
property IssuedAt: TDateTime read GetIssuedAt write SetIssuedAt;
/// <summary>
/// "jti" (JWT ID) Claim
/// The "jti" (JWT ID) claim provides a unique identifier for the JWT.
/// The identifier value MUST be assigned in a manner that ensures that
/// there is a negligible probability that the same value will be
/// accidentally assigned to a different data object; if the application
/// uses multiple issuers, collisions MUST be prevented among values
/// produced by different issuers as well. The "jti" claim can be used
/// to prevent the JWT from being replayed. The "jti" value is a case-
/// sensitive string. Use of this claim is OPTIONAL.
/// </summary>
property JWT_ID: string read GetJWT_ID write SetJWT_ID;
2016-05-18 18:21:43 +02:00
end;
TJWTCustomClaims = class(TJWTDictionaryObject)
property Items; default;
function AsCustomData: TMVCCustomData;
2016-05-18 18:21:43 +02:00
end;
TJWT = class
private
FSecretKey: string;
FRegisteredClaims: TJWTRegisteredClaims;
FCustomClaims: TJWTCustomClaims;
FHMACAlgorithm: string;
FRegClaimsToChecks: TJWTCheckableClaims;
FLeewaySeconds: Cardinal;
procedure SetHMACAlgorithm(const Value: string);
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;
procedure SetLiveValidityWindowInSeconds(const Value: Cardinal);
function GetLiveValidityWindowInSeconds: Cardinal;
2017-09-22 09:33:21 +02:00
function IsValidToken(const Token: string; out Header, Payload: TJSONObject; out Error: string): Boolean;
2016-05-18 18:21:43 +02:00
public
constructor Create(const SecretKey: string; const ALeewaySeconds: Cardinal = 300); virtual;
2016-05-18 18:21:43 +02:00
destructor Destroy; override;
function GetToken: string;
2017-09-22 09:33:21 +02:00
function LoadToken(const Token: string; out Error: string): Boolean;
2016-05-18 18:21:43 +02:00
property Claims: TJWTRegisteredClaims read FRegisteredClaims;
property CustomClaims: TJWTCustomClaims read FCustomClaims;
property HMACAlgorithm: string read FHMACAlgorithm write SetHMACAlgorithm;
property LeewaySeconds: Cardinal read FLeewaySeconds;
property RegClaimsToChecks: TJWTCheckableClaims read FRegClaimsToChecks write SetChecks;
/// <summary>
/// Use LiveValidityWindowInSeconds to make the ExpirationTime dynamic at each request.
/// ExpirationTime will be incremented by LiveValidityWindowInSeconds seconds automatically
/// if the remaining seconds are less than the LiveValidityWindowInSeconds.
/// </summary>
property LiveValidityWindowInSeconds: Cardinal read GetLiveValidityWindowInSeconds write SetLiveValidityWindowInSeconds;
2016-05-18 18:21:43 +02:00
end;
implementation
uses
System.SysUtils
, MVCFramework.Commons
, MVCFramework.HMAC
, System.DateUtils;
2016-05-18 18:21:43 +02:00
{ TJWTRegisteredClaims }
function TJWTRegisteredClaims.GetAudience: string;
2016-05-18 18:21:43 +02:00
begin
Result := Items[TJWTRegisteredClaimNames.Audience];
end;
function TJWTRegisteredClaims.GetExpirationTime: TDateTime;
begin
Result := ItemsAsDateTime[TJWTRegisteredClaimNames.ExpirationTime];
end;
function TJWTRegisteredClaims.GetIssuedAt: TDateTime;
begin
Result := ItemsAsDateTime[TJWTRegisteredClaimNames.IssuedAt];
end;
function TJWTRegisteredClaims.GetIssuer: string;
2016-05-18 18:21:43 +02:00
begin
Result := Items[TJWTRegisteredClaimNames.Issuer];
end;
function TJWTRegisteredClaims.GetJWT_ID: string;
2016-05-18 18:21:43 +02:00
begin
Result := Items[TJWTRegisteredClaimNames.JWT_ID];
end;
function TJWTRegisteredClaims.GetNotBefore: TDateTime;
begin
Result := ItemsAsDateTime[TJWTRegisteredClaimNames.NotBefore];
end;
function TJWTRegisteredClaims.GetSubject: string;
2016-05-18 18:21:43 +02:00
begin
Result := Items[TJWTRegisteredClaimNames.Subject];
end;
procedure TJWTRegisteredClaims.SetAudience(const Value: string);
2016-05-18 18:21:43 +02:00
begin
Items[TJWTRegisteredClaimNames.Audience] := Value;
end;
procedure TJWTRegisteredClaims.SetExpirationTime(const Value: TDateTime);
begin
ItemsAsDateTime[TJWTRegisteredClaimNames.ExpirationTime] := Value;
end;
procedure TJWTRegisteredClaims.SetIssuedAt(const Value: TDateTime);
begin
ItemsAsDateTime[TJWTRegisteredClaimNames.IssuedAt] := Value;
end;
procedure TJWTRegisteredClaims.SetISSUER(const Value: string);
2016-05-18 18:21:43 +02:00
begin
Items[TJWTRegisteredClaimNames.Issuer] := Value;
end;
procedure TJWTRegisteredClaims.SetJWT_ID(const Value: string);
2016-05-18 18:21:43 +02:00
begin
Items[TJWTRegisteredClaimNames.JWT_ID] := Value;
end;
procedure TJWTRegisteredClaims.SetNotBefore(const Value: TDateTime);
begin
ItemsAsDateTime[TJWTRegisteredClaimNames.NotBefore] := Value;
end;
procedure TJWTRegisteredClaims.SetSubject(const Value: string);
2016-05-18 18:21:43 +02:00
begin
Items[TJWTRegisteredClaimNames.Subject] := Value;
end;
{ TJWTCustomClaims }
function TJWTDictionaryObject.Contains(const Index: string): Boolean;
2016-05-18 18:21:43 +02:00
begin
Result := FClaims.ContainsKey(index);
2016-05-18 18:21:43 +02:00
end;
constructor TJWTDictionaryObject.Create;
begin
inherited;
FClaims := TDictionary<string, string>.Create;
2016-05-18 18:21:43 +02:00
end;
destructor TJWTDictionaryObject.Destroy;
begin
FClaims.Free;
inherited;
end;
function TJWTDictionaryObject.GetItem(const Index: string): string;
2016-05-18 18:21:43 +02:00
begin
if not FClaims.TryGetValue(index, Result) then
2016-05-18 18:21:43 +02:00
Result := '';
end;
function TJWTDictionaryObject.GetItemAsDateTime(const Index: string): TDateTime;
2016-05-18 18:21:43 +02:00
var
lIntValue: Int64;
begin
if not TryStrToInt64(Items[index], lIntValue) then
2016-05-18 18:21:43 +02:00
raise Exception.Create('Item cannot be converted as Unix Epoch');
Result := UnixToDateTime(lIntValue, False);
2016-05-18 18:21:43 +02:00
end;
function TJWTDictionaryObject.Keys: TArray<string>;
2016-05-18 18:21:43 +02:00
begin
Result := FClaims.Keys.ToArray;
end;
procedure TJWTDictionaryObject.SetItem(const Index, Value: string);
2016-05-18 18:21:43 +02:00
begin
FClaims.AddOrSetValue(index, Value);
2016-05-18 18:21:43 +02:00
end;
procedure TJWTDictionaryObject.SetItemAsDateTime(const Index: string;
2016-05-18 18:21:43 +02:00
const Value: TDateTime);
begin
Items[index] := IntToStr(DateTimeToUnix(Value, False));
2016-05-18 18:21:43 +02:00
end;
{ TJWTCustomClaims }
function TJWTCustomClaims.AsCustomData: TMVCCustomData;
begin
Result := TMVCCustomData.Create(FClaims);
end;
2016-05-18 18:21:43 +02:00
{ TJWT }
function TJWT.CheckExpirationTime(Payload: TJSONObject;
out Error: string): Boolean;
var
lJValue: TJSONValue;
lIntValue: Int64;
lValue: string;
lExpirationTimeAsDateTime: TDateTime;
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;
lExpirationTimeAsDateTime := UnixToDateTime(lIntValue, False);
if lExpirationTimeAsDateTime <= 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, False) >= 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, False) >= Now + FLeewaySeconds * OneSecond then
begin
Error := 'Token still not valid';
Exit(False);
end;
Result := True;
end;
constructor TJWT.Create(const SecretKey: string; const ALeewaySeconds: Cardinal = 300);
2016-05-18 18:21:43 +02:00
begin
inherited Create;
FSecretKey := SecretKey;
FRegisteredClaims := TJWTRegisteredClaims.Create;
FCustomClaims := TJWTCustomClaims.Create;
FHMACAlgorithm := HMAC_HS512;
FLeewaySeconds := ALeewaySeconds;
FRegClaimsToChecks := [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore,
TJWTCheckableClaim.IssuedAt];
2016-05-18 18:21:43 +02:00
end;
destructor TJWT.Destroy;
begin
FRegisteredClaims.Free;
FCustomClaims.Free;
inherited;
end;
function TJWT.GetLiveValidityWindowInSeconds: Cardinal;
begin
Result := StrToIntDef(FCustomClaims.Items['lvw'], 0);
end;
function TJWT.GetToken: string;
2016-05-18 18:21:43 +02:00
var
lHeader, lPayload: TJSONObject;
lHeaderEncoded, lPayloadEncoded, lToken, lHash: string;
2016-05-18 18:21:43 +02:00
lBytes: TBytes;
lRegClaimName: string;
lCustomClaimName: string;
2016-05-18 18:21:43 +02:00
begin
lHeader := TJSONObject.Create;
try
lPayload := TJSONObject.Create;
try
lHeader.AddPair('alg', HMACAlgorithm).AddPair('typ', 'JWT');
for lRegClaimName in TJWTRegisteredClaimNames.Names do
begin
if FRegisteredClaims.Contains(lRegClaimName) then
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;
2016-05-18 18:21:43 +02:00
end;
for lCustomClaimName in FCustomClaims.Keys do
begin
lPayload.AddPair(lCustomClaimName, FCustomClaims[lCustomClaimName]);
end;
lHeaderEncoded := URLSafeB64encode(lHeader.ToString, False);
lPayloadEncoded := URLSafeB64encode(lPayload.ToString, False);
2016-05-18 18:21:43 +02:00
lToken := lHeaderEncoded + '.' + lPayloadEncoded;
lBytes := HMAC(HMACAlgorithm, lToken, FSecretKey);
lHash := URLSafeB64encode(lBytes, false);
2016-05-18 18:21:43 +02:00
Result := lToken + '.' + lHash;
finally
lPayload.Free;
end;
finally
lHeader.Free;
end;
end;
2017-09-22 09:33:21 +02:00
function TJWT.IsValidToken(const Token: string; out Header, Payload: TJSONObject; out Error: string): Boolean;
2016-05-18 18:21:43 +02:00
var
lPieces: TArray<string>;
2016-05-18 18:21:43 +02:00
lJAlg: TJSONString;
lAlgName: string;
begin
2017-10-09 16:17:12 +02:00
Result := False;
Error := '';
2016-05-18 18:21:43 +02:00
lPieces := Token.Split(['.']);
if Length(lPieces) <> 3 then
begin
Error := 'Invalid Token';
Exit(False);
end;
2017-09-22 09:33:21 +02:00
Header := TJSONObject.ParseJSONValue(URLSafeB64Decode(lPieces[0])) as TJSONObject;
2016-05-18 18:21:43 +02:00
try
2017-09-22 09:33:21 +02:00
if not Assigned(Header) then
begin
Error := 'Invalid Token';
Exit(False);
end;
2016-05-18 18:21:43 +02:00
2017-09-22 09:33:21 +02:00
Payload := TJSONObject.ParseJSONValue(URLSafeB64Decode(lPieces[1])) as TJSONObject;
2016-05-18 18:21:43 +02:00
try
2017-09-22 09:33:21 +02:00
if not Assigned(Payload) then
begin
Error := 'Invalid Token';
Exit(False);
end;
2016-05-18 18:21:43 +02:00
2017-09-22 09:33:21 +02:00
if not Header.TryGetValue<TJSONString>('alg', lJAlg) then
begin
Error := 'Invalid Token';
Exit(False);
end;
2016-05-18 18:21:43 +02:00
lAlgName := lJAlg.Value;
Result := Token = lPieces[0] + '.' + lPieces[1] + '.' +
URLSafeB64encode(
HMAC(lAlgName, lPieces[0] + '.' + lPieces[1], FSecretKey),
False
2016-05-18 18:21:43 +02:00
);
// 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
2017-09-22 09:33:21 +02:00
if not CheckExpirationTime(Payload, Error) then
begin
Exit(False);
end;
end;
if TJWTCheckableClaim.NotBefore in RegClaimsToChecks then
begin
2017-09-22 09:33:21 +02:00
if not CheckNotBefore(Payload, Error) then
begin
Exit(False);
end;
end;
if TJWTCheckableClaim.IssuedAt in RegClaimsToChecks then
begin
2017-09-22 09:33:21 +02:00
if not CheckIssuedAt(Payload, Error) then
begin
Exit(False);
end;
end;
end;
2016-05-18 18:21:43 +02:00
finally
2017-09-22 09:33:21 +02:00
if not Result then
FreeAndNil(Payload);
2016-05-18 18:21:43 +02:00
end;
finally
2017-09-22 09:33:21 +02:00
if not Result then
FreeAndNil(Header);
2016-05-18 18:21:43 +02:00
end;
end;
2017-09-22 09:33:21 +02:00
function TJWT.LoadToken(const Token: string; out Error: string): Boolean;
2016-05-18 18:21:43 +02:00
var
lPieces: TArray<string>;
2016-05-18 18:21:43 +02:00
lJHeader: TJSONObject;
lJPayload: TJSONObject;
lJPair: TJSONPair;
i: Integer;
lName: string;
j: Integer;
lIsRegistered: Boolean;
lValue: string;
begin
2017-10-16 22:57:27 +02:00
lJHeader := nil;
lJPayload := nil;
2017-09-22 09:33:21 +02:00
Result := IsValidToken(Token, lJHeader, lJPayload, Error);
2016-05-18 18:21:43 +02:00
try
2017-09-22 09:33:21 +02:00
if not Result then
Exit(False);
lPieces := Token.Split(['.']);
// loading data from token into self
FHMACAlgorithm := lJHeader.Values['alg'].Value;
// registered claims
FRegisteredClaims.FClaims.Clear;
2016-05-18 18:21:43 +02:00
2017-09-22 09:33:21 +02:00
// custom claims
FCustomClaims.FClaims.Clear;
for i := 0 to lJPayload.Count - 1 do
begin
lIsRegistered := False;
lJPair := lJPayload.Pairs[i];
lName := lJPair.JsonString.Value;
lValue := lJPair.JsonValue.Value;
// if is a registered claim, load it in the proper dictionary...
for j := 0 to high(TJWTRegisteredClaimNames.Names) do
begin
if lName = TJWTRegisteredClaimNames.Names[j] then
2016-05-18 18:21:43 +02:00
begin
2017-09-22 09:33:21 +02:00
FRegisteredClaims.FClaims.AddOrSetValue(lName, lValue);
lIsRegistered := True;
Break;
2016-05-18 18:21:43 +02:00
end;
end;
2017-09-22 09:33:21 +02:00
if not lIsRegistered then
FCustomClaims.FClaims.AddOrSetValue(lName, lValue);
2016-05-18 18:21:43 +02:00
end;
2017-09-22 09:33:21 +02:00
FCustomClaims.FClaims.TrimExcess;
FRegisteredClaims.FClaims.TrimExcess;
2016-05-18 18:21:43 +02:00
finally
2017-09-22 09:33:21 +02:00
FreeAndNil(lJHeader);
FreeAndNil(lJPayload);
2016-05-18 18:21:43 +02:00
end;
end;
procedure TJWT.SetChecks(const Value: TJWTCheckableClaims);
begin
FRegClaimsToChecks := Value;
end;
procedure TJWT.SetHMACAlgorithm(const Value: string);
2016-05-18 18:21:43 +02:00
begin
FHMACAlgorithm := Value;
end;
procedure TJWT.SetLiveValidityWindowInSeconds(const Value: Cardinal);
begin
FCustomClaims.Items['lvw'] := Value.ToString;
end;
2016-05-18 18:21:43 +02:00
end.