Skip to content

Horse JWT

O Horse JWT é um middleware para você trabalhar com JWT em suas APIs.

⭕ Pré-requisitos

  • delphi-jose-jwt - JOSE é um padrão que fornece uma abordagem geral para a assinatura e criptografia de qualquer conteúdo.
  • hashlib4pascal - é uma biblioteca de hashing Object Pascal lançada sob a licença MIT permissiva que fornece uma interface fácil de usar para computar hashes e somas de verificação de dados. Ele também suporta hash baseado em estado (incremental).

⚙️ Instalação

Você pode instalar facilmente utilizando o comando boss install:

boss install horse-jwt

Ou, se você preferir instalar manualmente, basta adicionar as pastas em seu projeto, em Project > Options > Resource Compiler > Directories and Conditionals > Include file search path:

../horse-jwt/src
../delphi-jose-jwt/Source/Common
../delphi-jose-jwt/Source/JOSE
../horse-jwt/src
../HashLib/src/Base
../HashLib/src/Checksum
../HashLib/src/Crypto
../HashLib/src/Hash128
../HashLib/src/Hash32
../HashLib/src/Hash64
../HashLib/src/Include
../HashLib/src/Interfaces
../HashLib/src/KDF
../HashLib/src/NullDigest
../HashLib/src/Nullable
../HashLib/src/Packages
../HashLib/src/Utils

✔️ Compatibilidade

Delphi Lazarus
  ✔️    ✔️

⚡️ Início Rápido

  • Todas as solicitações precisam de autorização de token:
uses Horse, Horse.JWT;

begin
  THorse.Use(HorseJWT('MY-PASSWORD'));

  THorse.Post('ping',
    procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
    begin
      Res.Send('pong');
    end);

  THorse.Listen(9000);
end.
{$MODE DELPHI}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Horse, Horse.JWT, SysUtils;

procedure GetPing(Req: THorseRequest; Res: THorseResponse; Next: TNextProc);
begin
  Res.Send('Pong');
end;

begin
  THorse.Use(HorseJWT('my-private-key'));

  THorse.Get('/ping', GetPing);

  THorse.Listen(9000);
end.
  • Algumas rotas requerem autenticação:
uses Horse, Horse.JWT;

begin
  THorse.Get('ping',
    procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
    begin
      Res.Send('pong');
    end);

  THorse
    .AddCallback(HorseJWT('MY-PASSWORD'))
    .Get('private',
      procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
      begin
        Res.Send('route private');
      end);

  THorse.Listen(9000);
end.
{$MODE DELPHI}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Horse, Horse.JWT, SysUtils;

procedure GetPing(Req: THorseRequest; Res: THorseResponse; Next: TNextProc);
begin
  Res.Send('Pong');
end;

procedure GetPrivate(Req: THorseRequest; Res: THorseResponse; Next: TNextProc);
begin
  Res.Send('route private');
end;

begin
  THorse.Get('/ping', GetPing);

  THorse
    .AddCallback(HorseJWT('my-private-key'))
    .Get('private', GetPrivate);

  THorse.Listen(9000);
end.

🤙 Exemplos de Uso

Como criar um token?

uses
  Horse, Horse.JWT,
  JOSE.Core.JWT, JOSE.Core.Builder,
  System.DateUtils, System.SysUtils;

begin
  THorse.Post('create-token',
    procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
    var
      LJWT: TJWT;
      LToken: String;
    begin
      LJWT := TJWT.Create();
      try
        // Entre com os dados do Payload
        LJWT.Claims.Expiration := IncHour(Now, 1);

        // Gerando do Token
        LToken := TJOSE.SHA256CompactToken('MY-PASSWORD', LJWT);
      finally
        FreeAndNil(LJWT);
      end;

      // Enviando o Token
      Res.Send(LToken);
    end);

  THorse.Listen(9000);
end.

Como criar um Payload personalizado?

  • Aqui está um exemplo de uma classe de Payload personalizada.
  • Para os exemplos a seguir, é necessário usar esta classe.
unit MyClaims;

interface

uses
  JOSE.Core.JWT, JOSE.Types.JSON;

type
  TMyClaims = class(TJWTClaims)
  strict private
    function GetId: string;
    procedure SetId(const Value: string);
    function GetName: string;
    procedure SetName(const Value: string);
    function GetEmail: string;
    procedure SetEmail(const Value: string);
  public
    property Id: string read GetId write SetId;
    property name: string read GetName write SetName;
    property Email: string read GetEmail write SetEmail;
  end;

implementation

{ TMyClaims }

function TMyClaims.GetId: string;
begin
  Result := TJSONUtils.GetJSONValue('id', FJSON).AsString;
end;

procedure TMyClaims.SetId(const Value: string);
begin
  TJSONUtils.SetJSONValueFrom<string>('id', Value, FJSON);
end;

function TMyClaims.GetName: string;
begin
  Result := TJSONUtils.GetJSONValue('name', FJSON).AsString;
end;

procedure TMyClaims.SetName(const Value: string);
begin
  TJSONUtils.SetJSONValueFrom<string>('name', Value, FJSON);
end;

function TMyClaims.GetEmail: string;
begin
  Result := TJSONUtils.GetJSONValue('email', FJSON).AsString;
end;

procedure TMyClaims.SetEmail(const Value: string);
begin
  TJSONUtils.SetJSONValueFrom<string>('email', Value, FJSON);
end;

end.

Como criar um token com o Payload personalizado?

uses
  Horse, Horse.JWT,

  MyClaims in 'MyClaims.pas',

  JOSE.Core.JWT, JOSE.Core.Builder,
  System.DateUtils, System.SysUtils;

begin
  THorse.Post('create-token',
  procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
  var
    LJWT: TJWT;
    LClaims: TMyClaims;
    LToken: String;
  begin
    // Adicionar a Classe
    LJWT := TJWT.Create(TMyClaims);
    try
      // Casting usando a classe
      LClaims := TMyClaims(LJWT.Claims);

      // Entre com os dados do Payload
      LClaims.Expiration := IncHour(Now, 1);
      LClaims.Id := '1';
      LClaims.Name := 'Horse';
      LClaims.Email := 'horse@jwt.com';

      // Gerando o token
      LToken := TJOSE.SHA256CompactToken('MY-PASSWORD', LJWT);
    finally
      FreeAndNil(LJWT);
    end;

    // Enviando o token
    Res.Send(LToken);
  end);

  THorse.Listen(9000);
end.

Como ler um Payload personalizado

uses
  Horse, Horse.JWT,
  MyClaims in 'MyClaims.pas',
  System.SysUtils;

begin
  THorse
    .AddCallback(HorseJWT('MY-PASSWORD', TMyClaims)) // Adicionar a classe do Payload customizado
    .Get('ping',
      procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
      var
        LClaims: TMyClaims;
        LId, LName, LEmail: string;
      begin
        // Pegando as informações do Payload pela sessão
        LClaims := Req.Session<TMyClaims>;

        LId := LClaims.Id;
        LName := LClaims.Name;
        LEmail := LClaims.Email;

        Res.Send(Format('Eu sou %s e este é meu email %s',[LName, LEmail]));
      end);

  THorse.Listen(9000);
end.

Como criar rotas públicas e privadas?

uses
  Horse, Horse.JWT,

  MyClaims in 'MyClaims.pas',

  JOSE.Core.JWT, JOSE.Core.Builder,
  System.DateUtils, System.SysUtils;

begin
  // Criar token
  THorse.Post('create-token',
    procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
    var
      LJWT: TJWT;
      LClaims: TMyClaims;
      LToken: String;
    begin
      // Adicionar a classe
      LJWT := TJWT.Create(TMyClaims);
      try
        // Casting usando a classe
        LClaims := TMyClaims(LJWT.Claims);

        // Entrar com os dados do Payload
        LClaims.Expiration := IncHour(Now, 1);
        LClaims.Id := '1';
        LClaims.Name := 'Horse';
        LClaims.Email := 'horse@jwt.com';

        // Gerar o token
        LToken := TJOSE.SHA256CompactToken('MY-PASSWORD', LJWT);
      finally
        FreeAndNil(LJWT);
      end;

      // Enviar o token
      Res.Send(LToken);
    end);

  // Rota publica
  THorse.Get('public',
    procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
    begin
      Res.Send('Route public');
    end);

  // Rota privada
  THorse
    .AddCallback(HorseJWT('MY-PASSWORD', TMyClaims)) // Adicionar a classe do Payload customizado
    .Get('private',
      procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
      var
        LClaims: TMyClaims;
        LId, LName, LEmail: string;
      begin
        // Pegando as informações do Payload pela sessão
        LClaims := Req.Session<TMyClaims>;

        LId := LClaims.Id;
        LName := LClaims.Name;
        LEmail := LClaims.Email;

        Res.Send(Format('Eu sou %s e este é meu email %s', [LName, LEmail]));
      end);

  THorse.Listen(9000);
end.

⚠️ Avisos