ApiAuthentiationManager and RestSchemaDispatcher

SDK: 9.7.115.1441

I’m using JWT to authenticate access to my services using HttpApi.

And to achieve this I use my own ApiAuthenticationManager implementation.

I want to expose my DataTables as REST, similar to what is shown in [this topic]
(DataAbstract and new HttpApi).

But if enable RestSchemaDispatcher as shown in the sample my ApiAuthenticationManger code is never run for my REST routes.

Is there a way to have RestSchemaDispatcher and ApiAuthenticationManager play together?

Unfortunately no. You have to provide your own implementation, at least for now.

Thanks for your promt reply.

When you say I have to provide my own implementation, do you mean implement onLoginNeeded event? Or replace RestSchemaDispatcher with my own implementation?

Well, let’s look at this closer. All you actually need is to be able to call the REST dispatcher with your JWT session token and be able to actually execute the data requests. The issue is that RestSchemaDispatcher doesn’t understand JWT.

Take a look at this Schema Dispatcher implementation. It takes an incoming request, processes its JWT token and then passes it further. Wrapper classes are required to allow us to override the HTTP header values (particularly the one expected by the existing Schema Dispatcher)

public class JwtRestSchemaDispatcher : RestSchemaDispatcher
{
	public IApiAuthenticationManager AuthenticationManager { get; set; }

	public override void Process(IHttpRequest request, IHttpResponse response, Stream requestData, Stream responseData)
	{
		IApiSession apiSession;
		try
		{
			apiSession = this.AuthenticationManager.ReadAuthenticationInfo(request);
		}
		catch (Exception)
		{
			this.WriteNotAuthorized(response, responseData);
			return;
		}

		try
		{
			var requestWrapper = new RequestWrapper(request, apiSession.SessionId);

			base.Process(requestWrapper, response, requestData, responseData);
		}
		finally
		{
			// This call is required to let the authentication manager properly finalize the session
			// However at this pint we do not have an IApiMessage instance
			// So make sure that your Auth Manager implementation is able to properly manage WriteAuthenticationInfo
			// calls where IApiMessage instance was not provided
			this.AuthenticationManager.WriteAuthenticationInfo(response, null, apiSession);
		}
	}
}

sealed class RequestWrapper : IHttpRequest
{
	private readonly IHttpRequest _request;

	public RequestWrapper(IHttpRequest request, Guid sessionId)
	{
		this._request = request;
		this.Header = new HeaderWrapper(request.Header, sessionId);
	}

	public IHttpHeader Header { get; }

	public string Method
	{
		get
		{
			return this._request.Method;
		}
	}

	public string TargetUrl
	{
		get
		{
			return this._request.TargetUrl;
		}
		set
		{
			this._request.TargetUrl = value;
		}
	}

	public string ContentType
	{
		get
		{
			return this._request.ContentType;
		}
	}

	public string QueryString
	{
		get
		{
			return this._request.QueryString;
		}
	}

	public bool UsesAuthentication
	{
		get
		{
			return this._request.UsesAuthentication;
		}
		set
		{
			this._request.UsesAuthentication = value;
		}
	}

	public string AuthUsername
	{
		get
		{
			return this._request.AuthUsername;
		}
		set
		{
			this._request.AuthUsername = value;
		}
	}

	public string AuthPassword
	{
		get
		{
			return this._request.AuthPassword;
		}
		set
		{
			this._request.AuthPassword = value;
		}
	}

	public string GetQueryString(string value)
	{
		return this._request.GetQueryString(value);
	}
}

sealed class HeaderWrapper : IHttpHeader
{
	private readonly IHttpHeader _header;
	private readonly Guid _sessionId;

	public HeaderWrapper(IHttpHeader header, Guid sessionId)
	{
		this._header = header;
		this._sessionId = sessionId;
	}

	public string this[string name]
	{
		get
		{
			return (name == @"sessionid") ? (string)this._sessionId.ToString() : this._header[name];
		}
		set
		{
			// Left empty
		}
	}
}
1 Like

Take a look at this Schema Dispatcher implementation.

Wow, that’s perfect. I mean I thought I had to write my own implementation, but you saved me a ton of work.

bugs://83333 got closed with status fixed.

For further reference:

Authentication code has been moved into a separate virtual method named TryAcquireSesssion in both REST and OData dispatchers.

This allows to perform the same HttpAPI integration with way less code via overloading only TryAcquireSesssion and ReleaseSession methods.