unit LoginServiceImpl;

interface

uses
  System.SysUtils, System.IOUtils,
  LoginService,
  XData.Service.Common;

const
  // This is just an example! Change the secret for a production server
  JWTSecret = 'super_secret_0123456789_0123456789';

  // Set SignWithRSA to True to sig JWT usig asymmetric keys. In this case the JWT will be signed with a private key
  // ad later at the API will validated with the public key
  SignWithRSA = False;

  // Change KeyId if you recreate the private key
  RSAKeyId = 'D00CD046-FEDA-4120-9258-391371649A32';

type
  [ServiceImplementation]
  TLoginService = class(TInterfacedObject, ILoginService)
  public
    function Login(const User, Password: string): string;
  end;

implementation

uses
  Bcl.JOSE.Core.Builder,
  Bcl.Jose.Core.JWA,
  Bcl.JOSE.Core.JWT,
  Bcl.JOSE.Core.JWK,
  XData.Sys.Exceptions;

{ TLoginService }

function TLoginService.Login(const User, Password: string): string;
var
  JWT: TJWT;
  JWK: TJWK;
  SigningKey: TArray<Byte>;
  SigningAlgorithm: TJOSEAlgorithmId;
  Scopes: string;
begin
  // Check if user name and password are correct. This is your app business logic
  // Here, we will accept anything as long password is the same as user name.
  // Of course, don't use this logic in your app
  if User <> Password then
    raise EXDataHttpUnauthorized.Create('Invalid password');

  JWT := TJWT.Create;
  try
    // Add custom information (claims) to the JWT
    JWT.Claims.SetClaimOfType<string>('user', User);
    JWT.Claims.SetClaimOfType<boolean>('admin', User = 'admin');

    // Add the standard claim "scope", which usually work as "permissions"
    Scopes := 'reader';
    if (User = 'admin') or (User = 'writer') then
      Scopes := Scopes + ' writer';
    JWT.Claims.SetClaimOfType<string>('scope', Scopes);

    // Now sign the JWT with the proper algorithm, either shared secret (HS256) or private key (RSA256)
    if SignWithRSA then
    begin
      // load private key from file
      SigningKey := TFile.ReadAllBytes(TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), '..\..\..\keys\rsa256-private.key'));
      SigningAlgorithm := TJOSEAlgorithmId.RS256;
      Jwt.Header.KeyID := RSAKeyId;
    end
    else
    begin
      SigningKey := TEncoding.UTF8.GetBytes(JWTSecret);
      SigningAlgorithm := TJOSEAlgorithmId.HS256;
    end;

    JWK := TJWK.Create(SigningKey);
    try
      Result := TJOSE.SerializeCompact(JWK, SigningAlgorithm, JWT, False);
    finally
      JWK.Free;
    end;
  finally
    JWT.Free;
  end;
end;

initialization
  RegisterServiceType(TLoginService);
end.
