TMS software teams up with Malcolm Groves' new Code Partners company

Monday, May 30, 2016

  -  
It's with a lot of enthusiasm that we'd like to announce today that TMS software teams up with Malcolm Groves' new Code-Partners company. With this partnership, Code Partners becomes our exclusive representative for Australia, Japan and East-Asia. Code Partners will offer a full scale of services to assist you with your needs for application development and in relationship to TMS software products specifically, this includes:

  • help you choosing the TMS components and tools for your tasks at hand
  • offer expert-level consulting for integrating our components in your applications with a direct line to our R&D team
  • handle selling of our products in the Australia, Japan and East-Asia region
  • offer training on our components and tools
  • make extra information, insights, tips & tricks available on TMS components online via blog, videos, webinars,..

We're convinced that Malcolm's deep technical product knowledge, long-time experience with Delphi and software development in general and strong motivation is an excellent choice for a successful & long-term partnership that strives for a win³ goal:

  • a win for our customers
  • a win for Code Partners
  • a win for us
Visit Code Partners today and stay tuned!

Bruno Fierens




This blog post has received 2 comments.


1. Monday, May 30, 2016 at 10:21:57 AM

this doesn''t mean Australian developers are forced to buy their TMS products through Code Partners?

David Hughes


2. Monday, May 30, 2016 at 10:26:08 AM

No, it is an extra service offered by Code Partners but you can of course also continue to directly contact us for support, sales or any other topic as you have been used to.

Bruno Fierens




Add a new comment:
Author:
Email:
  You will receive a confirmation mail with a link to validate your comment, so please use a valid email address.
Comment:
 
Change Image
Fill in the characters from the image above:
 

All fields are required.
 



Presenting the TMS software website redesign

Monday, May 23, 2016

Since 1995, the year tmssoftware.com started focussing on offering components & tools for developers, it was time for a fifth major overhaul of our website. Since the beginning of this year, a lot work went into this overhaul behind the scenes for this overhaul. Some of the main goals of this website overhaul we set at the beginning of this project were:

  • Improved product discoverability
  • Graphically evolve into a more modern style, influenced by Apple, Google material design, Windows 10 style
  • Improved organisation of products by technologies and overview of product structure
  • Find sufficient differentiating and recognizable style elements
  • Move to responsive design for optimal UX for mobile, tablet, desktop
  • Improved readability of information with more space, more contrast, more consistent and better structure of product pages
  • Make the site more consistent with common web practices
Taking all these goals in consideration, you will understand the project was quite challenging. What's more, we wanted to respect the motto: evolution instead of revolution to also keep a high degree of familiarity for customers who know and use our website for all these years. Fortunately, we got the help from the company Well Communications. Here's a break-down of how all these goals were achieved:

Improved product discoverability

There is product discoverability from website top down navigation and from entering the website via search. Instead of showing the latest product releases, the home page is now centered around products. Products can be discovered from the main "Products" menu with a popup showing an overview of our most popular products organised by product type.



. At the same time, there is an overview of technologies/frameworks our products are based on and from there, the visitor is guided to the products per technology. Within the technology boxes on the main page, we have also featured our most popular products within this technology and the visitor can navigate to it directly by clicking it.



Equally important is discoverability from entering the website via searches. Here we have on product pages immediately references to equivalent products for different technologies. If you'd search for a cross platform scheduling/planner components via Google and were directed to the VCL product page, you can immediately see in the top right corner that we have the product also available for other technologies/frameworks.




Graphically evolve into a more modern style

Where the previous website was still more influenced by the Office 2007 style, early iOS style, it was a choice to move to a more minimal style typically used these days. We removed the old DVD box analogy representing the software, we removed gradients, grayish & soft elements and now have a more sharp, slightly harder graphical design.

Improved organisation of products

There is a strong focus on organisation of products by technology or framework. This is already visible on the main page where we have currently 8 boxes representing 8 technologies/frameworks we have products for. Each technology or framework is represented by a specific color and these colors should help increase the awareness under what technology a product can be used.



Some products support multiple technologies/frameworks. Whereas this was not possible in the former website, now, products supporting multiple frameworks at the same time will be listed under each technology/framework these support. As such, our ORM product TMS Aurelius will appear under VCL, FMX and the business logic category as it falls into each of these categories.

Differentiating and recognizable style elements

Our logo has been slightly restyled with the underlining of the part 'tmssoftware' and ending the line with a corner under the dot. Where a square logo is needed like for avatars, this is reduced to 'tms' underlined ending under the dot.






This line & dot element is repeated for various boxes on our website and subtle animations stress this line & dot element.



We also like to think of the dot on the line a kind of similar to putting the dot on the i, hinting at our drive and continuous striving for delivering our products and support with the highest possible quality.

Responsive design

We had noticed that about 20% of our website traffic was coming from mobile devices & tablets, so we considered this important enough to also focus on offering a good UX for users on mobile phones and tablets. Our website will now automatically adapt to have a good usability on all these different browser form factors.



Improved readability

Where in our former website, the product information or blogs were often squeezed between the left sidebar and the right-side download box or blog history & selector, we removed the boxes at the right side of these pages and we add the capability to hide the left sidebar. This way, the full page can be used to read our product information and blogs and have this space available for bigger screenshots as well.

Consistency with common web practices

We paid attention to keep our website consistent with typical & common web practices these days. You'll find the website sign-in button in the top right corner of the page. You find the company information, social media links, website sitemap and commonly used page references always the bottom of the website in the footer area. In a similar way, after login the website will always display your user account name on top as well as the main menu in a fixed non-scrolling area. A search functionality across our product pages, web forum and FAQs is also available and always visible in the top right corner.




In a nutshell, we hope you will not only find our v5.0 website more visually pleasing but also easier to navigate and discover products. While v5 was tested and approved by a small group of people in the past couple of weeks, as of today, it is available for everyone. This means that from today, we're eager to learn from you how you experience the v5 website and more importantly, if you have more ideas and suggestions to improve it even further. With your inputs and help, we can continue to strive for perfection or "to put the dot on the line" in our case. But at the same time,keeping the famous words of painter Salvador Dali in mind: "Have no fear of perfection, you will never reach it".



Bruno Fierens




This blog post has received 17 comments.


1. Monday, May 23, 2016 at 3:53:14 PM

Much better, congratulations.

Marcos Douglas


2. Monday, May 23, 2016 at 7:31:51 PM

Nice design. Good Job.

Are you thinking about an App for support your forums?

Cilleruelo Gonzalo Juan C.


3. Monday, May 23, 2016 at 8:12:35 PM

I''m not sure what you had envisioned as "app for support"?

Bruno Fierens


4. Tuesday, May 24, 2016 at 12:51:46 AM

Well done!

Jiri


5. Tuesday, May 24, 2016 at 4:30:45 AM

I want use chrome browser .
chrome browser is NG.
Internet Explore is OK.

Isao Hirai


6. Tuesday, May 24, 2016 at 8:30:41 AM

Please press F5 to force Google to refresh the cached CSS

Bruno Fierens


7. Tuesday, May 24, 2016 at 12:48:20 PM

Great redesign!
Very nice work!

Malinovszky László


8. Tuesday, May 24, 2016 at 1:19:10 PM

Like!

Bossier Edwin


9. Tuesday, May 24, 2016 at 3:12:33 PM

Great job, beautiful, modern and clean design.

Thumbs up!

Karel Janecek


10. Wednesday, May 25, 2016 at 12:01:23 PM

Great Design! Very Clean and perfect Navigation !

Lindau Andreas


11. Friday, May 27, 2016 at 3:32:44 PM

You have now a big problem with the forums:

Getting messages like this:
Error
You do not have permission to reply to posts in this forum

or this:
Error Error
You must be a registered forum member in order to post in this forum.

is the norm.

Good design but a bad job in back.


Cilleruelo Gonzalo Juan C.


12. Friday, May 27, 2016 at 3:34:50 PM

With respect to forum rules, NOTHING changed, so, make sure you are logged in as customer and you should be able to post, reply, ... in the forum.
If there is a doubt, force a logout and login again.

Bruno Fierens


13. Friday, May 27, 2016 at 3:36:38 PM

I''ve tried disconnect an back to connect. But this don''t solve the problem.
Seems like if my account has been blocked.

Have you any limit for questions in the forum?

Cilleruelo Gonzalo Juan C.


14. Friday, May 27, 2016 at 3:37:37 PM

I''ve tried disconnect an back to connect. But this don''t solve the problem.
Seems like if my account has been blocked.

Have you any limit for questions in the forum?

Cilleruelo Gonzalo Juan C.


15. Friday, May 27, 2016 at 3:38:05 PM

Your account is NOT blocked. It works without ANY issue from here. If a problem persists on your side, contact us by email, a blog comment is NOT the place to deal with this kind of support.

Bruno Fierens


16. Friday, May 27, 2016 at 3:41:14 PM

Is not question of rules, but question of running.
I''ve made logout and login again....

I can''t do new entries in the forum.

Cilleruelo Gonzalo Juan C.


17. Friday, May 27, 2016 at 3:41:34 PM

Last response: contact us by email, here is NOT the place to deal with this kind of support!

Bruno Fierens




Add a new comment:
Author:
Email:
  You will receive a confirmation mail with a link to validate your comment, so please use a valid email address.
Comment:
 
Change Image
Fill in the characters from the image above:
 

All fields are required.
 



Developing your first FNC custom control

Friday, May 13, 2016

Some weeks ago, we released the TMS FNC UI Pack, a set of Framework Neutral Components (FNC), i.e. UI controls that can be used from VCL Windows applications, FireMonkey (FMX) Windows, Mac OS-X, iOS, Android applications and LCL framework based Lazarus applications for Windows, Linux, Mac OS-X,..
The TMS FNC UI Pack contains highly complex & feature-rich components such as grid, planner, rich editor, treeview, toolbars. To create such complex components that work under 3 frameworks and a myriad of operating systems is not a trivial excercise and requires intricate knowledge about the VCL, FMX and LCL frameworks as well as the operating systems the controls need to work under.
To help ourselves and the users of the TMS FNC UI Pack, we have introduced several abstractions that facilitate creating framework neutral components and this is what we want to cover in this brief introduction to developing FNC custom controls.

FNC custom control basics

The structure of the FNC custom control we want to present is this of a classic UI control. The control is responsible for painting itself and interacts with keyboard and/or mouse. The control has several properties to control its appearance and behavior. If we look at this concept from the perspective of implementing this for 3 different frameworks, the biggest challenges faced are:

1) abstractions in the code for dealing with graphics: especially VCL and FMX are quite different in this respect, so abstraction is welcome.
2) abstractions in keyboard and mouse handling: also here there are differences, although subtle, between VCL, FMX and LCL.
3) abstractions in types: types such as font, color, rectangles, points are different in FMX and VCL.

So, we'll cover what is included in FNC to facilitate dealing with this.

Unit organisation

Perhaps the biggest stumbling block is unit organisation. As it is desirable to create components that can be installed in the IDE, (which means for Delphi for both the VCL and FireMonkey framework simultanously) we'll need units for VCL and units for FireMonkey. Although we can use the same class name, the class hierarchy for the VCL control and the FMX control will be different. The VCL FNC control will descend from the VCL TCustomControl and the FMX FNC control will descend from the FMX TControl. In Lazarus, the FNC control will descend from the LCL TCustomControl. In a nutshell, to solve this, we create 3 units for a component that will be nearly identical and we provide a conversion step to allow you to write the code in one unit and automatically generate the other units for the other frameworks. For this example, in total, we'll have 6 units: 3 units with the code for the control for the 3 supported frameworks and 3 units for the component registration in the IDE:

// Units for the VCL variant of the FNC control
VCL.TMSFNCCust.pas
VCL.TMSFNCCustReg.pas

// Units for the FMX variant of the FNC control
FMX.TMSFNCCust.pas
FMX.TMSFNCCustReg.pas

// Units for the LCL variant of the FNC control
LCLTMSFNCCust.pas
LCLTMSFNCCustReg.pas
We'll also use 3 packages: a package for VCL, a package for FMX and a package for LCL. We can install the VCL & FMX package simultanously in the Delphi IDE and the LCL package in the Lazarus IDE. The package for the custom control will have a dependency to the framework plus to the TMS FNC UI Pack for the framework. The structure of the packages in the Delphi 10.1 Berlin IDE is:


and in Lazarus, this is:

Getting to grips

Ok, now that the unit structure is setup, we can focus on writing the code. To write this code, we'll use 3 TMS FNC UI Pack units with abstractions: xxx.TMSFNCCustomControl, xxx.TMSFNCGraphics and xxx.TMSFNCTypes (with xxx = the framework). In this example, we'll write the code in VCL and automatically generate the FMX & LCL equivalents from it, so the uses list becomes:

for FMX
uses
  Classes, Types, FMX.TMSFNCCustomControl, FMX.TMSFNCGraphics, FMX.TMSFNCTypes;
for LCL
uses
  Classes, Types, LCLTMSFNCCustomControl, LCLTMSFNCGraphics, LCLTMSFNCTypes;
The FNC control we'll write here is very rudimentary for the sake of focusing on the abstractions. It is basically a gauge control that can be clicked with the mouse to set the gauge value, has a gauge value property to set it and can use the key up & down to change the gauge value. So, we need painting of the gauge, we need some gauge line color & font settings and we need handling of the mouse click and keyboard.


Control initialization

The control descends from the abstract FNC class TTMSFNCCustomControl and exposes one extra color property for the gauge line color (note that this are of the abstract type TTMSFNCGraphicsColor), a font (of the abstract type TTMSFNCGraphicsFont, that has also a font color in all frameworks) and a gauge value property of the type TControlValue. Note that the Stroke & Fill properties are published. This contains the control border & control background color and even enables things such as setting a background gradient, a border with a specific pen style etc...

  TControlValue = 0..100;

  TTMSFNCCustomControlSample = class(TTMSFNCCustomControl)
  private
    FLineColor: TTMSFNCGraphicsColor;
    FFont: TTMSFNCGraphicsFont;
    FValue: TControlValue;
  protected
    procedure Draw({%H-}AGraphics: TTMSFNCGraphics; {%H-}ARect: TRectF); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Stroke;
    property Fill;
    property Font: TTMSFNCGraphicsFont read FFont write SetFont;
    property LineColor: TTMSFNCGraphicsColor read FLineColor write SetLineColor default gcRed;
    property Value: TControlValue read FValue write SetValue default 0;
  end;
In the constructor, initialization of the line color property value is done as well as the border color and the font is created. This is of the type TTMSFNCGraphicsFont and the main purpose is to have a font with font color in all frameworks. There is one more thing particular in the constructor and that is the assignment of the Font.OnChange event handler. In Delphi Pascal, we can simply assign the object method but for the FPC compiler, this needs to be prefixed with the @ symbol. Fortunately, the Lazarus environment has the LCL define we can use to handle this.

{ TTMSFNCCustomControlSample }

constructor TTMSFNCCustomControlSample.Create(AOwner: TComponent);
begin
  inherited;
  Stroke.Color := gcBlack;
  FLineColor := gcRed;
  FFont := TTMSFNCGraphicsFont.Create;
  FFont.OnChanged := {$IFDEF LCL}@{$ENDIF}FontChanged;
  Width := 100;
  Height := 100; 
end;

destructor TTMSFNCCustomControlSample.Destroy;
begin
  FFont.Free;
  inherited;
end;
Painting

The control descends from the abstract FNC class TTMSFNCCustomControl and exposes 3 color properties, the border, background and gauge line color (note that these are of the type TTMSFNCGraphicsColor), a font (of the type TTMSFNCGraphicsFont, that has also a font color in all frameworks) and a gauge value property of the type TControlValue.

Painting is done in the override of the Draw() method that has 2 parameters: AGraphics: TTMSFNCGraphics, a framework neutral graphics library and the rectangle of the control via ARect of the type TRectF. In VCL and LCL only the not fractional part of the floating point numbers is used but of course in the FireMonkey framework, this can use the fractional parts as well.

The painting code itself is:

procedure TTMSFNCCustomControlSample.Draw(AGraphics: TTMSFNCGraphics;
  ARect: TRectF);
var
  angle: double;
  lf,tf: TPointF;
  w: single;
begin
  inherited;

  angle := Value/High(Value)*2*PI;

  w := Min(ARect.Right - ARect.Left, ARect.Bottom - ARect.Top) / 2;

  lf.X := (ARect.Right - ARect.Left)/2;
  lf.Y := (ARect.Bottom - ARect.Top)/2;

  tf.X := lf.X + Cos(angle) * w;
  tf.Y := lf.Y - Sin(angle) * w;

  AGraphics.Stroke.Color := LineColor;

  AGraphics.DrawLine(lf,tf);

  AGraphics.Font.Assign(Font);
  AGraphics.DrawText(ARect, InttoStr(Value),false, gtaCenter, gtaCenter);
end;

Mouse and keyboard handling

The mouse and keyboard handling is via the HandleKeyDown and HandleMouseDown virtual method overrides:

  TTMSFNCCustomControlSample = class(TTMSFNCCustomControl)
  protected
    procedure HandleKeyDown(var {%H-}Key: Word; {%H-}Shift: TShiftState); override;
    procedure HandleMouseDown({%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Single); override;
  end;
The implementation is straightforward. One thing that is noteworthy, is that the unit xxx.TTMSFNCTypes has a list of keys in a framework and operating system neutral way, i.e. we can check for the arrow up and arrow down keys with the constants KEY_DOWN and KEY_UP.

procedure TTMSFNCCustomControlSample.HandleKeyDown(var Key: Word;
  Shift: TShiftState);
begin
  inherited;

  if Key = KEY_DOWN then
  begin
    if Value > Low(Value) then
      Value := Value - 1;
  end;

  if Key = KEY_UP then
  begin
    if Value < High(Value) then
      Value := Value + 1;
  end;
end;
In the mouse down handler, we set the focus to the control when possible and then calculate the value of the gauge that matches with the mouse click position. Note that the X,Y values are of the type single to accomodate FMX and will contain non fractional values in VCL and LCL.

procedure TTMSFNCCustomControlSample.HandleMouseDown(Button: TTMSFNCMouseButton;
  Shift: TShiftState; X, Y: Single);
var
  angle: single;
  dx,dy: single;

begin
  inherited;

  if AllowFocus then
    SetFocus;

  dx := x - (Width/2);
  dy := - y + (Height/2);

  if dx = 0 then
    angle := sign(dy) * PI / 2
  else
    angle := ArcTan(dy/dx);

  if dx < 0 then
    angle := PI + angle;

  if angle < 0 then
    angle := angle + 2 * PI;

  Value := Round((angle / 2 / PI) * High(Value));
end;
Creating the units for FMX and LCL

Now that we have the VCL framework FNC component ready that contains 100% framework neutral code, let's create automatically the FMX and LCL units from this. 3 steps are needed:

1) Rename the unit VCL.TMSFNCCust.pas to FMX.TMSFNCCust.pas and LCLTMSFNCCust.pas
2) Change in the unit .PAS file the unit name, i.e. replace VCL.TMSFNCCust by FMX.TMSFNCCust and LCLTMSFNCCust respectively
3) Change the unit references in the uses list from
VCL.TMSFNCCustomControl, VCL.TMSFNCGraphics, VCL.TMSFNCTypes;
to
FMX.TMSFNCCustomControl, FMX.TMSFNCGraphics, FMX.TMSFNCTypes;
or
LCLTMSFNCCustomControl, LCLTMSFNCGraphics, LCLTMSFNCTypes;

To accomplish this, we call a simple powershell script that performs text replacement from VCL.TMS to FMX.TMS or LCLTMS respectively:

powershell -command "(gc VCL.TMSFNCCust.pas) -replace 'VCL.TMS','LCLTMS' |Out-file LCLTMSFNCCust.pas -Encoding utf8"
powershell -command "(gc VCL.TMSFNCCust.pas) -replace 'VCL.TMS','FMX.TMS' |Out-file FMX.TMSFNCCust.pas -Encoding utf8"
With these units created, we can compile the packages and install our FNC custom control for use from VCL, FMX and LCL applications in Lazarus.


The full source of this sample FNC custom control can be downloaded here. This sample can be used with the latest version of the TMS FNC UI Pack.

We hope this already whets your appetite for exploring FNC and the power of writing code for UI controls once for use in 3 frameworks. See also this blog article for a more general coverage of what is available in the TMS FNC UI Pack. In a next article, we'll go deeper in compound control creation and also the TTMSFNCGraphics library that offers a vast range of functions, going from drawing text, polygons, polylines, images in various formats, controls like checkboxes, radiobuttons, buttons, ... and much more.

Bruno Fierens




This blog post has received 7 comments.


1. Friday, May 13, 2016 at 1:27:43 PM

Nice seeing a truly platform and compiler independent class hierarchy of visual components.

But I wonder, why use 3 separated units in private projects?
It does make sense for new components internal to the TNC suite (each platform has its own folder), but for custom components, it may not be worth it, and error-prone, if you forget to generate the LCL/FMX units after a modification to the VCL unit.

Why not use a single unit, but with $ifdef for the uses clause?
Sounds like outside the "uses", the code is 100% platform-independent, e.g. all classes inheriting from TTMSFNCCustomControl.

A. Bouchez


2. Friday, May 13, 2016 at 2:35:37 PM

The reason is explained in the article and related to a Delphi control hierarchy being different for different frameworks & package limitations.

Bruno Fierens


3. Friday, May 13, 2016 at 4:28:33 PM

The reason invoked does not make sense for private use.
Only the "uses" clauses need to be customized.

A single unit with the following is enough:
uses
Classes, Types
{$ifdef USEFMX}
, FMX.TMSFNCCustomControl, FMX.TMSFNCGraphics, FMX.TMSFNCTypes;
{$endif}
{$ifdef USELCL}
, LCLTMSFNCCustomControl, LCLTMSFNCGraphics, LCLTMSFNCTypes;
{$endif}
{$ifdef USEVCL}
, VCL.TMSFNCCustomControl, VCL.TMSFNCGraphics, VCL.TMSFNCTypes;
{$endif}

(of course, USELCL/USEVCL/USEFMX are to be replaced by the proper conditionals)
No need to create 3 diverse units in end-user code base. This single unit could then be included in every project, with no problem.

A. Bouchez


4. Friday, May 13, 2016 at 4:40:34 PM

What is up with the {%H-} ?

C Johnson


5. Friday, May 13, 2016 at 7:47:17 PM

Mr. Bouchez,
The problem is installing the components in the IDE!
With a single unit solution, this DOES NOT WORK to install in the IDE for BOTH VCL and FMX.
End of discussion.

Bruno Fierens


6. Friday, May 13, 2016 at 7:47:56 PM

Mr. Johsnon,
This is something to suppress warnings for Lazarus/FPC.

Bruno Fierens


7. Tuesday, May 24, 2016 at 8:58:44 AM

Nice!

Kosinski Tomasz




Add a new comment:
Author:
Email:
  You will receive a confirmation mail with a link to validate your comment, so please use a valid email address.
Comment:
 
Change Image
Fill in the characters from the image above:
 

All fields are required.
 



Rest Server Authentication using JWT (JSON Web Token)

Friday, May 06, 2016

When building HTTP Servers with TMS Business frameworks - more specifically Rest/JSON servers with TMS XData - you can use the builtin authentication and authorization mechanism to protect server resources/requests. Such mechanism is actually implemented in our lower-level framework TMS Sparkle and can be used for any types of HTTP server, not only XData. This blog post will show you how to use authorization and authentication using JSON Web Tokens.

JSON Web Token (JWT)

From Wikipedia: "JSON Web Token (JWT) is a JSON-based open standard (RFC 7519) for passing claims between parties in web application environment". That doesn't say much if we have never heard about it before. There is plenty of information out there to read more in details about JWT, so here I'm going directly to the point in a very summarized practical way.

A JWT is a string with this format:

aaaaaaaaaaa.bbbbbbbbbb.cccccccccc

It's just three sections in string separated by dots. Each section is a text encoded using base64-url:

(base64url-encoded header).(base64url-encoded claims).(base64url-encoded signature)

So a real JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidG1zdXNlciIsImlzcyI6IlRNUyBYRGF0YSBTZXJ2ZXIiLCJhZG1pbiI6dHJ1ZX0.
pb-4JAajpYxTsDTqWtgyIgpoqCQH8wlHl4RoTki8kpQ

If we decode each part of the JWT separately (remember, we have three parts separated by dots), this is what we would have from part one (spaces and returns added to make it more readable). It's the header:

{
  "alg":"HS256", 
  "typ":"JWT"
}

And this is part two decoded, which is the payload or claims set:

{
  "name":"tmsuser",
  "iss":"TMS XData Server",
  "admin":true
}

Finally the third part is the signature. It makes no sense to decode it here since it's just a bunch of bytes that represent the hash of three things: the header, the payload, and the secret that only the generator of the JWT knows.

The payload is the JSON object that "matters", it's the actual content that end-user applications will read to perform actions. The header contains meta information of the token, mostly the hashing algorithm used to generate the signature, also present in the token. So, we could say that a JWT is just an alternative way to represent a JSON object, but with a signature attached to it.

What does it has to do with authentication and authorization? Well, you can think of the JWT as a "session" or "context" for an user accessing your server. The JSON object in the payload will contain arbitrary information that you are going to put in there, like permissions, user name, etc. This token will be generated by your server upon some event (for example, an user "login"), and then the client will resend the token to the server whenever he wants to perform any operation. This would be the basic workflow:

1. Client performs "login" in the server by passing regular user credentials (user name and password for example)
2. The server validates the credentials, generates a JWT with relevant info, using the secret, and sends the JWT back to the client
3. The client sends the JWT in next requests, passing the JWT again to the server
4. When processing each request, the server checks if the JWT signature is valid. If it is, then it can trust that the JSON Object in payload is valid and can proceed normally

Since only the server has the secret, there is no way the client can change the payload, adding false information to it - for example, change the user name or permissions. When the server receives the modified JWT, the signature will not match and the token will be rejected by the server.

For more detailed information on JSON Web Tokens (JWT) you can refer to https://jwt.io, the Wikipedia article or just the official specification. It's also worth mentioning that for handling JWT internally, either to create or validate the tokens, TMS XData uses under the hood the open source Delphi JOSE and JWT library.

JWT Authentication with TMS XData

Enough of theory, the next steps will show you how to implement authentication/authorization using JWT in TMS XData. This is just a suggestion of implementation, and it's up to you to define with more details how your system will work. In this example we will create a login service, add the middleware and use XData server-side events to implement authorization.

User Login and JWT Generation

We're going to create a service operation to allow users to perform login. Our service contract will look like this:

  [ServiceContract]
  ILoginService = interface(IInvokable)
  ['{BAD477A2-86EC-45B9-A1B1-C896C58DD5E0}']
    function Login(const UserName, Password: string): string;
  end;

Users will send user name and password, and receive the token. Delphi applications can invoke this method using the TXDataClient, or invoke it using regular HTTP, performing a POST request passing user name and password parameters in the body request in JSON format. Nevertheless, be aware that you should always use a secure connection (HTTPS) in your server to protect such requests.

The implementation of such service operation would be something like this:

uses {...}, Bcl.Jose.Core.JWT, Bcl.Jose.Core.Builder;
 
function TLoginService.Login(const UserName, Password: string): string;
var
  JWT: TJWT;
  Role: string;
  IsAdmin: Boolean;
begin
  { check if UserName and Password are valid, retrieve User data from database, 
   add relevant claims to JWT and return it. In this example, we will only
   add two claims: Role and IsAdmin.  }
  
  // Now that application specific logic is finished, generate the token 
  JWT := TJWT.Create(TJWTClaims);
  try
    JWT.Claims.SetClaimOfType<string>('role', Role);
    JWT.Claims.SetClaimOfType<string>('isadmin', IsAdmin);
    JWT.Claims.Issuer := 'XData Server';
    Result := TJOSE.SHA256CompactToken('secret', JWT);
  finally
    JWT.Free;
  end;
end;

Now, users can simply login to the server by performing a request like this (some headers removed):

POST /loginservice/login HTTP/1.1
content-type: application/json
 
{
  "UserName": "tmsuser",
  "Password": "tmsuser"
}

And the response will be a JSON object containing the JSON Web Token (some headers removed and JWT modified for better readibility):

HTTP/1.1 200 OK
Content-Type: application/json
 
{
    "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6InRtc3VzZXIiLCJpc3MiOiJYRGF0YSBTZXJ2ZXIifQ.CAxxa3aizZheG3VXmBoXtfdg3N5jN9tNAZHEV7R-W4Q"
}

For further requests, clients just need to add that token in the request using the authorization header by indicating it's a bearer token. For example:

GET /customers?$orderby=Name HTTP/1.1
content-type: application/json
authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6InRtc3VzZXIiLCJpc3MiOiJYRGF0YSBTZXJ2ZXIifQ.CAxxa3aizZheG3VXmBoXtfdg3N5jN9tNAZHEV7R-W4Q

Adding TJwtMiddleware to process tokens in requests

The second step is to add the TJwtMiddleware to our XData server module. It's done just once, before starting up the server with the module. All we need to inform is the secret our middleware will use to validate the signature of the received tokens:

uses {...}, Sparkle.Middleware.Jwt;
 
{...}
  Module.AddMiddleware(TJwtMiddleware.Create('secret'));

That's it. What this will do? It will automatically check for the token in the authorization header. If it does exist and signature is valid, it will create the IUserIdentity interface, set its Claims based on the claims in the JWT, and set such interface to the User property of THttpRequest object. Regardless if the token exists or not and the User property is set or not, the middleware will forward the processing of the request to your server anyway. It's up to you to check if user is present in the request and perform the correct actions. The only situation where the middleware will return immediately is if the token is present and is invalid (bad format or wrong signature). In this case it will return an error to the client immediately and your server code will not be executed.

Authorizing the requests

Finally, in your application code, all you have to do is check for such User property and take actions based on it. For example, suppose you have a service operation DoSomething that runs some arbitrary code. You don't want to allow anonymous requests (not authenticated) to execute that operation. And you will also only execute it if authenticated user is an administrator. This is how you would implement it:

uses {...}, Sparkle.Security, XData.Sys.Exceptions;
 
procedure TMyService.DoSomething;
var
  User: IUserIdentity;
begin
  User := TXDataOperationContext.Current.Request.User;
  if User = nil then
    raise EXDataHttpUnauthorized.Create('User not authenticated');
  if not (User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean) then
    raise EXDataHttpForbidden.Create('Not enough privileges');
  
  // if code reachs here, user is authenticated and is administrador
  // execute the action
end;

Server-side events

The above code is valid for service operations. But you can also use server-side events to protect the entity sets published by XData. For example, you can use the OnEntityDeleting event to not allow non-admin users to delete resources. The event handler implementation would be pretty much the same as the code above (Module refers to a TXDataServerModule object):

  Module.Events.OnEntityDeleting.Subscribe(procedure(Args: TEntityDeletingArgs)
    var User: IUserIdentity;
    begin
      User := TXDataOperationContext.Current.Request.User;
      if User = nil then
        raise EXDataHttpUnauthorized.Create('User not authenticated');
      if not (User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean) then
        raise EXDataHttpForbidden.Create('Not enough privileges');
    end
  );

That applies to all entities published by the server. Of course, if you want to restrict the code to some entities, you can check the Args.Entity property to verify the class of object being deleted and perform actions accordingly.

Finally, another nice example for authorization and server-side events. Suppose that every entity in your application has a property named "Protected" which means only admin users can see those entities. You can use a code similar to the one above to refuse requests that try to modify, create or retrieve a protected entity if the requesting user is not admin.

But what about complex queries that return multiple entities? In this case you can use the OnEntityList event, which will provide you with the Aurelius criteria (TCriteria) that will be used to retrieve the entities. You can then modify such criteria depending on user permissions:

  Module.Events.OnEntityList.Subscribe(procedure(Args: TEntityListArgs)
    var 
      User: IUserIdentity;
      IsAdmin: Boolean;
    begin
      User := Args.Handler.Request.User;
      IsAdmin := (User <> nil) and User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean;
      if not IsAdmin then
        Args.Criteria.Add(TLinq.EqualsTo('Protected', false));
    end
  );

The code above simply checks if the requesting user has elevated privileges. If it does not, then it adds an extra condition to the criteria (whatever the criteria is) which filters only the entities that are not protected. So non-admin users will not see the protected entities in the server response.


For more information about the subject, here is a summary of the links related to the topics covered in this post:
TMS Business Subscription Page
TMS XData Product Page
TMS Sparkle Product Page
TMS XData Online Documentation
TMS Sparkle Online Documentation
JSON Web Token Web Site
Wikipedia article about JSON Web Token
JSON Web Token Specification - RFC-7519
Delphi JOSE and JWT library



Wagner R. Landgraf




This blog post has received 3 comments.


1. Friday, May 06, 2016 at 8:11:00 PM

Hi Wagner

WOW!!! Thank you very much. This is a great feature!! ;-)

Pederzolli Patrizia


2. Monday, May 09, 2016 at 10:17:55 PM

Thanks a lot.
This makes TMS Business Suite so much more worth - great.

Roland Kossow


3. Wednesday, May 25, 2016 at 3:51:22 PM

Tenho acompanhado a evolução do Sparkle e me impressiona a sintonia com as necessidades dos desenvolvedores. A pouco tempo, o release do sistema middleware já havia dado maturidade plena ao framework e agora somos surpreendidos com a adição deste mecanismo de autenticação. Parabéns a TMS!

Wagner, aproveito para fazer uma pergunta: O uso da biblioteca open source "Delphi JOSE" significa que o sparkle JWT depende das DLLs OpenSSL no servidor?

Geziel Machado




Add a new comment:
Author:
Email:
  You will receive a confirmation mail with a link to validate your comment, so please use a valid email address.
Comment:
 
Change Image
Fill in the characters from the image above:
 

All fields are required.
 




Previous  |  Next  |  Index