2016-06-22 17:49:16 +02:00
|
|
|
// ***************************************************************************
|
|
|
|
//
|
|
|
|
// Delphi MVC Framework
|
|
|
|
//
|
2019-01-08 12:48:27 +01:00
|
|
|
// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team
|
2016-06-22 17:49:16 +02:00
|
|
|
//
|
|
|
|
// 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-23 17:21:33 +02:00
|
|
|
|
|
|
|
unit MVCFramework.Middleware.JWT;
|
|
|
|
|
2016-12-30 20:41:55 +01:00
|
|
|
{$I dmvcframework.inc}
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
interface
|
2016-12-30 20:41:55 +01:00
|
|
|
|
2016-05-23 17:21:33 +02:00
|
|
|
uses
|
2017-03-23 18:51:25 +01:00
|
|
|
System.SysUtils,
|
|
|
|
System.Classes,
|
|
|
|
System.Generics.Collections,
|
2016-05-23 17:21:33 +02:00
|
|
|
MVCFramework,
|
|
|
|
MVCFramework.Commons,
|
2019-03-06 17:38:26 +01:00
|
|
|
MVCFramework.JWT,
|
|
|
|
JsonDataObjects;
|
2016-05-23 17:21:33 +02:00
|
|
|
|
|
|
|
type
|
2019-06-11 19:42:03 +02:00
|
|
|
TMVCJWTDefaults = class sealed
|
|
|
|
public const
|
|
|
|
/// <summary>
|
2019-07-07 17:25:11 +02:00
|
|
|
/// Default authorization header name
|
2019-06-11 19:42:03 +02:00
|
|
|
/// </summary>
|
|
|
|
AUTHORIZATION_HEADER = 'Authentication';
|
|
|
|
/// <summary>
|
2019-07-07 17:25:11 +02:00
|
|
|
/// Default username header name
|
2019-06-11 19:42:03 +02:00
|
|
|
/// </summary>
|
|
|
|
USERNAME_HEADER = 'jwtusername';
|
|
|
|
/// <summary>
|
2019-07-07 17:25:11 +02:00
|
|
|
/// Default password header name
|
2019-06-11 19:42:03 +02:00
|
|
|
/// </summary>
|
|
|
|
PASSWORD_HEADER = 'jwtpassword';
|
|
|
|
end;
|
2017-03-20 19:08:01 +01:00
|
|
|
|
2016-06-23 11:42:16 +02:00
|
|
|
TJWTClaimsSetup = reference to procedure(const JWT: TJWT);
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
TMVCJWTAuthenticationMiddleware = class(TInterfacedObject, IMVCMiddleware)
|
2016-05-23 17:21:33 +02:00
|
|
|
private
|
2017-03-23 18:51:25 +01:00
|
|
|
FAuthenticationHandler: IMVCAuthenticationHandler;
|
2016-05-23 17:21:33 +02:00
|
|
|
FClaimsToChecks: TJWTCheckableClaims;
|
2016-06-23 11:42:16 +02:00
|
|
|
FSetupJWTClaims: TJWTClaimsSetup;
|
2017-03-23 18:51:25 +01:00
|
|
|
FSecret: string;
|
2017-07-12 00:31:49 +02:00
|
|
|
FLeewaySeconds: Cardinal;
|
2017-09-07 00:10:21 +02:00
|
|
|
FLoginURLSegment: string;
|
2019-06-11 19:42:03 +02:00
|
|
|
FAuthorizationHeaderName: string;
|
|
|
|
FUserNameHeaderName: string;
|
|
|
|
FPasswordHeaderName: string;
|
2017-03-30 21:22:54 +02:00
|
|
|
protected
|
2018-05-17 21:55:32 +02:00
|
|
|
function NeedsToBeExtended(const JWTValue: TJWT): Boolean;
|
|
|
|
procedure ExtendExpirationTime(const JWTValue: TJWT);
|
2019-03-06 17:38:26 +01:00
|
|
|
procedure InternalRender(AJSONOb: TJDOJsonObject; AContentType: string; AContentEncoding: string;
|
2018-05-17 21:55:32 +02:00
|
|
|
AContext: TWebContext; AInstanceOwner: Boolean = True);
|
|
|
|
|
2019-07-08 19:25:56 +02:00
|
|
|
procedure RenderError(const AHTTPStatusCode: UInt16; const AErrorMessage: string;
|
|
|
|
const AContext: TWebContext; const AErrorClassName: string = ''; const AErrorNumber: Integer = 0);
|
2018-05-17 21:55:32 +02:00
|
|
|
|
|
|
|
procedure OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
|
|
|
|
|
|
|
|
procedure OnBeforeControllerAction(AContext: TWebContext; const AControllerQualifiedClassName: string;
|
|
|
|
const AActionName: string; var AHandled: Boolean);
|
|
|
|
|
|
|
|
procedure OnAfterControllerAction(AContext: TWebContext; const AActionName: string; const AHandled: Boolean);
|
2016-05-23 17:21:33 +02:00
|
|
|
public
|
2019-06-11 19:42:03 +02:00
|
|
|
constructor Create(
|
|
|
|
AAuthenticationHandler: IMVCAuthenticationHandler;
|
|
|
|
AConfigClaims: TJWTClaimsSetup;
|
|
|
|
ASecret: string = 'D3lph1MVCFram3w0rk';
|
|
|
|
ALoginURLSegment: string = '/login';
|
2019-07-07 17:25:11 +02:00
|
|
|
AClaimsToCheck: TJWTCheckableClaims = [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore,
|
|
|
|
TJWTCheckableClaim.IssuedAt];
|
2019-06-11 19:42:03 +02:00
|
|
|
ALeewaySeconds: Cardinal = 300;
|
|
|
|
AAuthorizationHeaderName: string = TMVCJWTDefaults.AUTHORIZATION_HEADER;
|
|
|
|
AUserNameHeaderName: string = TMVCJWTDefaults.USERNAME_HEADER;
|
|
|
|
APasswordHeaderName: string = TMVCJWTDefaults.PASSWORD_HEADER); virtual;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
2018-05-17 21:55:32 +02:00
|
|
|
uses
|
|
|
|
System.NetEncoding,
|
|
|
|
System.DateUtils,
|
2019-07-07 17:25:11 +02:00
|
|
|
System.Math,
|
|
|
|
MVCFramework.Logger;
|
2017-07-12 00:31:49 +02:00
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
{ TMVCJWTAuthenticationMiddleware }
|
2016-05-23 17:21:33 +02:00
|
|
|
|
2017-07-12 00:31:49 +02:00
|
|
|
constructor TMVCJWTAuthenticationMiddleware.Create(AAuthenticationHandler: IMVCAuthenticationHandler;
|
2019-06-11 19:42:03 +02:00
|
|
|
AConfigClaims: TJWTClaimsSetup;
|
|
|
|
ASecret: string = 'D3lph1MVCFram3w0rk';
|
|
|
|
ALoginURLSegment: string = '/login';
|
2019-07-07 17:25:11 +02:00
|
|
|
AClaimsToCheck: TJWTCheckableClaims = [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore,
|
|
|
|
TJWTCheckableClaim.IssuedAt];
|
2019-06-11 19:42:03 +02:00
|
|
|
ALeewaySeconds: Cardinal = 300;
|
|
|
|
AAuthorizationHeaderName: string = TMVCJWTDefaults.AUTHORIZATION_HEADER;
|
|
|
|
AUserNameHeaderName: string = TMVCJWTDefaults.USERNAME_HEADER;
|
|
|
|
APasswordHeaderName: string = TMVCJWTDefaults.PASSWORD_HEADER);
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
|
|
|
inherited Create;
|
2017-03-23 18:51:25 +01:00
|
|
|
FAuthenticationHandler := AAuthenticationHandler;
|
|
|
|
FSetupJWTClaims := AConfigClaims;
|
|
|
|
FClaimsToChecks := AClaimsToCheck;
|
|
|
|
FSecret := ASecret;
|
2017-07-14 17:38:51 +02:00
|
|
|
FLoginURLSegment := ALoginURLSegment;
|
2017-07-12 00:31:49 +02:00
|
|
|
FLeewaySeconds := ALeewaySeconds;
|
2019-06-11 19:42:03 +02:00
|
|
|
FAuthorizationHeaderName := AAuthorizationHeaderName;
|
|
|
|
FUserNameHeaderName := AUserNameHeaderName;
|
|
|
|
FPasswordHeaderName := APasswordHeaderName;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
|
2018-05-17 21:55:32 +02:00
|
|
|
procedure TMVCJWTAuthenticationMiddleware.ExtendExpirationTime(const JWTValue: TJWT);
|
|
|
|
begin
|
|
|
|
JWTValue.Claims.ExpirationTime := Max(JWTValue.Claims.ExpirationTime, Now) +
|
|
|
|
(JWTValue.LeewaySeconds + JWTValue.LiveValidityWindowInSeconds) * OneSecond;
|
|
|
|
end;
|
|
|
|
|
2019-03-06 22:28:22 +01:00
|
|
|
procedure TMVCJWTAuthenticationMiddleware.InternalRender(AJSONOb: TJDOJsonObject;
|
|
|
|
AContentType, AContentEncoding: string; AContext: TWebContext; AInstanceOwner: Boolean);
|
2017-03-20 19:08:01 +01:00
|
|
|
var
|
2017-03-23 18:51:25 +01:00
|
|
|
Encoding: TEncoding;
|
|
|
|
ContentType, JValue: string;
|
2017-03-20 19:08:01 +01:00
|
|
|
begin
|
2019-03-06 17:38:26 +01:00
|
|
|
JValue := AJSONOb.ToJSON;
|
2017-03-20 19:08:01 +01:00
|
|
|
|
|
|
|
AContext.Response.RawWebResponse.ContentType := AContentType + '; charset=' + AContentEncoding;
|
2017-03-23 18:51:25 +01:00
|
|
|
ContentType := AContentType + '; charset=' + AContentEncoding;
|
|
|
|
|
|
|
|
Encoding := TEncoding.GetEncoding(AContentEncoding);
|
2017-03-20 19:08:01 +01:00
|
|
|
try
|
2018-05-17 21:55:32 +02:00
|
|
|
AContext.Response.SetContentStream(TBytesStream.Create(TEncoding.Convert(TEncoding.Default, Encoding,
|
|
|
|
TEncoding.Default.GetBytes(JValue))), ContentType);
|
2017-03-20 19:08:01 +01:00
|
|
|
finally
|
2017-03-23 18:51:25 +01:00
|
|
|
Encoding.Free;
|
2017-03-20 19:08:01 +01:00
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
if AInstanceOwner then
|
2019-03-06 17:38:26 +01:00
|
|
|
FreeAndNil(AJSONOb)
|
2017-03-20 19:08:01 +01:00
|
|
|
end;
|
|
|
|
|
2018-05-17 21:55:32 +02:00
|
|
|
function TMVCJWTAuthenticationMiddleware.NeedsToBeExtended(const JWTValue: TJWT): Boolean;
|
|
|
|
var
|
|
|
|
lWillExpireIn: Int64;
|
|
|
|
begin
|
|
|
|
lWillExpireIn := SecondsBetween(Now, JWTValue.Claims.ExpirationTime);
|
|
|
|
Result := lWillExpireIn <= JWTValue.LiveValidityWindowInSeconds;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMVCJWTAuthenticationMiddleware.OnAfterControllerAction(AContext: TWebContext; const AActionName: string;
|
2017-03-23 18:51:25 +01:00
|
|
|
const AHandled: Boolean);
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
// Implement as needed
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
|
2018-05-17 21:55:32 +02:00
|
|
|
procedure TMVCJWTAuthenticationMiddleware.OnBeforeControllerAction(AContext: TWebContext;
|
|
|
|
const AControllerQualifiedClassName, AActionName: string; var AHandled: Boolean);
|
2016-05-23 17:21:33 +02:00
|
|
|
var
|
2017-03-23 18:51:25 +01:00
|
|
|
AuthRequired: Boolean;
|
|
|
|
IsAuthorized: Boolean;
|
|
|
|
JWTValue: TJWT;
|
|
|
|
AuthHeader: string;
|
|
|
|
AuthToken: string;
|
2017-07-12 00:31:49 +02:00
|
|
|
ErrorMsg: string;
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
|
|
|
// check if the resource is protected
|
2018-12-09 23:03:06 +01:00
|
|
|
FAuthenticationHandler.OnRequest(AContext, AControllerQualifiedClassName, AActionName, AuthRequired);
|
2017-03-23 18:51:25 +01:00
|
|
|
|
|
|
|
if not AuthRequired then
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
AHandled := False;
|
2016-05-23 17:21:33 +02:00
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Checking token in subsequent requests
|
|
|
|
// ***************************************************
|
2017-07-12 00:31:49 +02:00
|
|
|
JWTValue := TJWT.Create(FSecret, FLeewaySeconds);
|
2016-05-23 17:21:33 +02:00
|
|
|
try
|
2017-03-23 18:51:25 +01:00
|
|
|
JWTValue.RegClaimsToChecks := Self.FClaimsToChecks;
|
2019-06-11 19:42:03 +02:00
|
|
|
AuthHeader := AContext.Request.Headers[FAuthorizationHeaderName];
|
2017-03-23 18:51:25 +01:00
|
|
|
if AuthHeader.IsEmpty then
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2019-06-11 19:42:03 +02:00
|
|
|
RenderError(HTTP_STATUS.Unauthorized, 'Authorization Required', AContext);
|
2017-03-23 18:51:25 +01:00
|
|
|
AHandled := True;
|
2016-05-23 17:21:33 +02:00
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
|
2016-06-23 11:42:16 +02:00
|
|
|
// retrieve the token from the "authentication bearer" header
|
2017-03-23 18:51:25 +01:00
|
|
|
AuthToken := '';
|
|
|
|
if AuthHeader.StartsWith('bearer', True) then
|
2017-05-17 22:32:45 +02:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
AuthToken := AuthHeader.Remove(0, 'bearer'.Length).Trim;
|
2019-01-18 18:11:27 +01:00
|
|
|
AuthToken := Trim(TNetEncoding.URL.Decode(AuthToken));
|
2017-05-17 22:32:45 +02:00
|
|
|
end;
|
2016-05-23 17:21:33 +02:00
|
|
|
|
2017-09-22 09:33:21 +02:00
|
|
|
if not JWTValue.LoadToken(AuthToken, ErrorMsg) then
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
RenderError(HTTP_STATUS.Unauthorized, ErrorMsg, AContext);
|
|
|
|
AHandled := True;
|
2017-09-22 09:33:21 +02:00
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if JWTValue.CustomClaims['username'].IsEmpty then
|
|
|
|
begin
|
2019-06-11 19:42:03 +02:00
|
|
|
RenderError(HTTP_STATUS.Unauthorized, 'Invalid Token, Authorization Required', AContext);
|
2017-09-22 09:33:21 +02:00
|
|
|
AHandled := True;
|
2016-05-23 17:21:33 +02:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
2017-09-22 09:33:21 +02:00
|
|
|
IsAuthorized := False;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
2017-09-22 09:33:21 +02:00
|
|
|
AContext.LoggedUser.UserName := JWTValue.CustomClaims['username'];
|
|
|
|
AContext.LoggedUser.Roles.AddRange(JWTValue.CustomClaims['roles'].Split([',']));
|
|
|
|
AContext.LoggedUser.LoggedSince := JWTValue.Claims.IssuedAt;
|
|
|
|
AContext.LoggedUser.CustomData := JWTValue.CustomClaims.AsCustomData;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
2019-03-06 22:28:22 +01:00
|
|
|
FAuthenticationHandler.OnAuthorization(AContext, AContext.LoggedUser.Roles, AControllerQualifiedClassName,
|
|
|
|
AActionName, IsAuthorized);
|
2017-03-23 18:51:25 +01:00
|
|
|
|
2017-09-22 09:33:21 +02:00
|
|
|
if IsAuthorized then
|
|
|
|
begin
|
|
|
|
if JWTValue.LiveValidityWindowInSeconds > 0 then
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2018-05-17 21:55:32 +02:00
|
|
|
if NeedsToBeExtended(JWTValue) then
|
|
|
|
begin
|
|
|
|
ExtendExpirationTime(JWTValue);
|
2019-06-11 19:42:03 +02:00
|
|
|
AContext.Response.SetCustomHeader(FAuthorizationHeaderName, 'bearer ' + JWTValue.GetToken);
|
2018-05-17 21:55:32 +02:00
|
|
|
end;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
2017-09-22 09:33:21 +02:00
|
|
|
AHandled := False
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
RenderError(HTTP_STATUS.Forbidden, 'Authorization Forbidden', AContext);
|
|
|
|
AHandled := True;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
finally
|
2017-03-23 18:51:25 +01:00
|
|
|
JWTValue.Free;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2018-05-17 21:55:32 +02:00
|
|
|
procedure TMVCJWTAuthenticationMiddleware.OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
|
2016-05-23 17:21:33 +02:00
|
|
|
var
|
2017-03-23 18:51:25 +01:00
|
|
|
UserName: string;
|
|
|
|
Password: string;
|
2017-07-12 00:31:49 +02:00
|
|
|
RolesList: TList<string>;
|
2017-03-23 18:51:25 +01:00
|
|
|
SessionData: TSessionData;
|
|
|
|
IsValid: Boolean;
|
|
|
|
JWTValue: TJWT;
|
2017-09-07 00:10:21 +02:00
|
|
|
lCustomPair: TPair<string, string>;
|
2019-03-06 17:38:26 +01:00
|
|
|
LObj: TJDOJsonObject;
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2017-07-14 17:38:51 +02:00
|
|
|
if SameText(AContext.Request.PathInfo, FLoginURLSegment) and (AContext.Request.HTTPMethod = httpPOST) then
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2019-06-11 19:42:03 +02:00
|
|
|
UserName := TNetEncoding.URL.Decode(AContext.Request.Headers[FUserNameHeaderName]);
|
|
|
|
Password := TNetEncoding.URL.Decode(AContext.Request.Headers[FPasswordHeaderName]);
|
2017-03-23 18:51:25 +01:00
|
|
|
if (UserName.IsEmpty) or (Password.IsEmpty) then
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
RenderError(HTTP_STATUS.Unauthorized, 'Username and password Required', AContext);
|
|
|
|
AHandled := True;
|
2016-05-23 17:21:33 +02:00
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// check the authorization for the requested resource
|
2017-03-23 18:51:25 +01:00
|
|
|
RolesList := TList<string>.Create;
|
2016-05-23 17:21:33 +02:00
|
|
|
try
|
2017-03-23 18:51:25 +01:00
|
|
|
SessionData := TSessionData.Create;
|
2016-05-23 17:21:33 +02:00
|
|
|
try
|
2019-03-06 17:38:26 +01:00
|
|
|
try
|
|
|
|
FAuthenticationHandler.OnAuthentication(AContext, UserName, Password, RolesList, IsValid, SessionData);
|
|
|
|
if IsValid then
|
|
|
|
begin
|
|
|
|
JWTValue := TJWT.Create(FSecret, FLeewaySeconds);
|
|
|
|
try
|
|
|
|
// let's user config claims and custom claims
|
|
|
|
if not Assigned(FSetupJWTClaims) then
|
|
|
|
raise EMVCJWTException.Create('SetupJWTClaims not set');
|
|
|
|
|
|
|
|
FSetupJWTClaims(JWTValue);
|
|
|
|
|
|
|
|
// these claims are mandatory and managed by the middleware
|
|
|
|
if not JWTValue.CustomClaims['username'].IsEmpty then
|
2019-03-06 22:28:22 +01:00
|
|
|
raise EMVCJWTException.Create
|
|
|
|
('Custom claim "username" is reserved and cannot be modified in the JWT setup');
|
2019-03-06 17:38:26 +01:00
|
|
|
|
|
|
|
if not JWTValue.CustomClaims['roles'].IsEmpty then
|
2019-03-06 22:28:22 +01:00
|
|
|
raise EMVCJWTException.Create
|
|
|
|
('Custom claim "roles" is reserved and cannot be modified in the JWT setup');
|
2019-03-06 17:38:26 +01:00
|
|
|
|
|
|
|
JWTValue.CustomClaims['username'] := UserName;
|
|
|
|
JWTValue.CustomClaims['roles'] := string.Join(',', RolesList.ToArray);
|
|
|
|
|
|
|
|
if JWTValue.LiveValidityWindowInSeconds > 0 then
|
2018-05-17 21:55:32 +02:00
|
|
|
begin
|
2019-03-06 17:38:26 +01:00
|
|
|
if NeedsToBeExtended(JWTValue) then
|
|
|
|
begin
|
|
|
|
ExtendExpirationTime(JWTValue);
|
|
|
|
end;
|
2018-05-17 21:55:32 +02:00
|
|
|
end;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
2019-03-06 17:38:26 +01:00
|
|
|
// setup the current logged user from the JWT
|
|
|
|
AContext.LoggedUser.Roles.AddRange(RolesList);
|
|
|
|
AContext.LoggedUser.UserName := JWTValue.CustomClaims['username'];
|
|
|
|
AContext.LoggedUser.LoggedSince := JWTValue.Claims.IssuedAt;
|
|
|
|
AContext.LoggedUser.Realm := JWTValue.Claims.Subject;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
2019-03-06 17:38:26 +01:00
|
|
|
if SessionData.Count > 0 then
|
2017-09-07 00:10:21 +02:00
|
|
|
begin
|
2019-03-06 17:38:26 +01:00
|
|
|
AContext.LoggedUser.CustomData := TMVCCustomData.Create;
|
|
|
|
for lCustomPair in SessionData do
|
|
|
|
begin
|
|
|
|
AContext.LoggedUser.CustomData.AddOrSetValue(lCustomPair.Key, lCustomPair.Value);
|
|
|
|
if not JWTValue.CustomClaims.Items[lCustomPair.Key].IsEmpty then
|
|
|
|
raise EMVCJWTException.CreateFmt('JWT Error: "%s" is a reserved key name', [lCustomPair.Key]);
|
|
|
|
JWTValue.CustomClaims.Items[lCustomPair.Key] := lCustomPair.Value;
|
|
|
|
end;
|
2017-09-07 00:10:21 +02:00
|
|
|
end;
|
|
|
|
|
2019-03-06 17:38:26 +01:00
|
|
|
LObj := TJDOJsonObject.Create;
|
2019-03-06 22:28:22 +01:00
|
|
|
try
|
|
|
|
LObj.S['token'] := JWTValue.GetToken;
|
|
|
|
InternalRender(LObj, TMVCMediaType.APPLICATION_JSON, TMVCConstants.DEFAULT_CONTENT_CHARSET,
|
|
|
|
AContext, False);
|
|
|
|
finally
|
|
|
|
LObj.Free;
|
|
|
|
end;
|
2019-03-06 17:38:26 +01:00
|
|
|
AHandled := True;
|
|
|
|
finally
|
2019-03-06 22:28:22 +01:00
|
|
|
JWTValue.Free;
|
2019-03-06 17:38:26 +01:00
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
RenderError(HTTP_STATUS.Forbidden, 'Forbidden', AContext);
|
2017-03-23 18:51:25 +01:00
|
|
|
AHandled := True;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
2019-03-06 17:38:26 +01:00
|
|
|
except
|
2019-07-07 17:25:11 +02:00
|
|
|
on Err: EMVCException do
|
2019-03-06 17:38:26 +01:00
|
|
|
begin
|
2019-07-08 19:25:56 +02:00
|
|
|
RenderError(Err.HttpErrorCode, Err.Message, AContext, Err.ClassName, Err.ApplicationErrorCode);
|
2019-07-07 17:25:11 +02:00
|
|
|
AHandled := True;
|
|
|
|
end;
|
|
|
|
on e: Exception do
|
|
|
|
begin
|
|
|
|
RenderError(HTTP_STATUS.Forbidden, e.Message, AContext);
|
2019-03-06 17:38:26 +01:00
|
|
|
AHandled := True;
|
|
|
|
end;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
finally
|
2017-03-23 18:51:25 +01:00
|
|
|
SessionData.Free;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
finally
|
2017-03-23 18:51:25 +01:00
|
|
|
RolesList.Free;
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2019-07-08 19:25:56 +02:00
|
|
|
procedure TMVCJWTAuthenticationMiddleware.RenderError(const AHTTPStatusCode: UInt16; const AErrorMessage: string;
|
|
|
|
const AContext: TWebContext; const AErrorClassName: string; const AErrorNumber: Integer);
|
2016-05-23 17:21:33 +02:00
|
|
|
var
|
2019-07-08 19:25:56 +02:00
|
|
|
lJObj: TJDOJsonObject;
|
|
|
|
lStatus: string;
|
2016-05-23 17:21:33 +02:00
|
|
|
begin
|
2019-07-08 19:25:56 +02:00
|
|
|
AContext.Response.StatusCode := AHTTPStatusCode;
|
2017-03-23 18:51:25 +01:00
|
|
|
AContext.Response.ReasonString := AErrorMessage;
|
|
|
|
|
2019-07-08 19:25:56 +02:00
|
|
|
lStatus := 'error';
|
|
|
|
if (AHTTPStatusCode div 100) = 2 then
|
|
|
|
lStatus := 'ok';
|
2017-03-23 18:51:25 +01:00
|
|
|
|
2019-07-08 19:25:56 +02:00
|
|
|
lJObj := TJDOJsonObject.Create;
|
|
|
|
lJObj.S['status'] := lStatus;
|
|
|
|
lJObj.I['statuscode'] := AHTTPStatusCode;
|
|
|
|
lJObj.S['message'] := AErrorMessage;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
|
|
|
if AErrorClassName = '' then
|
2019-07-08 19:25:56 +02:00
|
|
|
begin
|
|
|
|
lJObj.Values['classname'] := nil
|
|
|
|
end
|
2016-05-23 17:21:33 +02:00
|
|
|
else
|
2019-07-08 19:25:56 +02:00
|
|
|
begin
|
|
|
|
lJObj.S['classname'] := AErrorClassName;
|
|
|
|
end;
|
2016-05-23 17:21:33 +02:00
|
|
|
|
|
|
|
|
2019-07-08 19:25:56 +02:00
|
|
|
if AErrorNumber <> 0 then
|
|
|
|
begin
|
|
|
|
lJObj.I['errornumber'] := AErrorNumber;
|
|
|
|
end;
|
|
|
|
|
|
|
|
InternalRender(lJObj, TMVCConstants.DEFAULT_CONTENT_TYPE, TMVCConstants.DEFAULT_CONTENT_CHARSET, AContext);
|
2016-05-23 17:21:33 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|