In SynCrypto.pas and SynEcc.pas, you will find:

  • TJWTAbstract as abstract parent class for implementing JSON Web Tokens;
  • TJWTNone implementing the "none" algorithm;
  • TJWTHS256 implementing the "HS256" algorithm;
  • TJWTES256 implementing the "ES256" algorithm.

To generate JWT, you may use

var j: TJWTAbstract;
    jwt: TJWTContent;
...
j := TJWTHS256.Create('secret',0,[jrcSubject],[]);
try
j.Verify('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibm'+
    'FtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeF'+
    'ONFh7HgQ',jwt); // reference from jwt.io
  check(jwt.result=jwtValid);
  check(jwt.reg[jrcSubject]='1234567890');
  check(jwt.data.U['name']='John Doe');
  check(jwt.data.B['admin']);
finally
  j.Free;
end;

The 'eyJhbGciOiJIUzI1NiIsIn...' token contains in fact the following, once base-64 decoded:

  • header: {"alg":"HS256","typ":"JWT"}
  • payload: {"sub":"1234567890","name":"John Doe","admin":true}
  • signature: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), "secret")

It has a built-in support of claims, so you can write:

j := TJWTHS256.Create('sec',10,[jrcIssuer,jrcExpirationTime,jrcIssuedAt,jrcJWTID],[],60);
token := one.Compute(['http://example.com/is_root',true],'joe');

Now, token contains e.g. as signed payload:

{"http://example.com/is_root":true,"iss":"joe","iat":1482177879,"exp":1482181479,"jti":"1496DCE0676925DD33BB5A81"}

Then you can decode such a token, and access its payload in a single method:

j.Verify(token,jwt);
assert(jwt.result=jwtValid);
assert(jwt.reg[jrcIssuer]='joe');

Integration with method-based services is easy, using Ctxt.AuthenticationCheck method:

TMyDaemon = class(...
protected
  fJWT: TJWTAbstract;
  ....
procedure TMyDaemon.Files(Ctxt: TSQLRestServerURIContext);
begin
  if Ctxt.AuthenticationCheck(fJWT)=jwtValid then
    Ctxt.ReturnFileFromFolder('c:\datafolder');
end;

The above method will define a method-based service returning the content of a local folder, only if a valid JWT is supplied within the HTTP headers of the incoming request.

Feel free to discuss about this new feature in our forum, and check the latest version of the documentation!