REST services allow clients to access server resources by exchanging representational data state using predefined HTTP methods. REST provides several advantages over other web service frameworks including scalability, simplicity, and flexibility.
However, with the increasing use of REST services, comes the need for secure authentication. Since RESTful applications are stateless, they do not have the benefit of session management that traditional web applications have. This creates challenges for implementing secure authentication mechanisms. Any authentication mechanism implemented for REST services must be able to identify the user and maintain their identity across multiple requests.
In this documentation, we will explore various methods for securing REST services authentication. We will discuss the advantages and disadvantages of each method, as well as provide guidance for choosing the appropriate authentication mechanism for your application needs. The documentation will also cover best practices for implementing secure authentication in a RESTful environment, including the use of SSL/TLS encryption and token-based authentication mechanisms. By following the guidelines outlined in this documentation, you can ensure that your RESTful applications are secure and able to provide seamless and reliable service to clients.
REST services use JSON Web Tokens to enable flexible integration scenarios. Authentication methods produce these tokens that represent that the user is recognized by the service as valid and is allowed to perform operations on it for a set period of time.
This information is secured by cryptographic means by digitally signing the JWT token with a private key, making it impossible to tamper with the contents of the JWT and ensuring that it was produced exclusively by the REST service.
This token is used in HTTP Authentication headers of subsequent operation calls, propagating trust to whoever owns the token. Since ownership is sensitive the token provides three main mechanisms to work with:
While the service will set a maximum expiration timespan for the tokens, the service users should request as short timeout as its viable for their operations to be carried out.
REST services do not allow for token revocation mechanisms, since they would require state by definition. Using short timeouts or lease token refreshing mechanisms is the best way to secure and mitigate against replay attacks while maintaining top performance and scalability for the service.
Additionally, JWT tokens contain more information that is used to further secure their access scope:
Use of REST services often occurs during preset integration windows, where some information is being processed as a batch and sent to the REST service. Moreover, both the client application and the service are usually in the same secure network that is not exposed to the internet or human user access.
In this scenario a token can be requested once for the expected duration of the integration window. To reduce the integration complexity, it's also viable to use more predictable batches and request a token for each one of them.
If the batches are processed in parallel by multiple hosts its recommended that each one requests its own token and that no communication or information sharing transmits the token.
Another scenario is where client applications need to integrate with REST services in real time. In this scenario the operations are expected to be much shorter in duration but a lot more frequent during the day.
The recommended approach here involves requesting a relatively short duration token using full authentication, then using that token to request more refresh tokens when the previous one is about to expire.
Using some token managing class is recommended to avoid having the performance hit of having to request a refresh token per operation.
User facing applications are the most security critical points of an infrastructure. Trust should be limited as much as possible and information restricted to the bare minimum necessary for the user operation.
We recommend that in this scenario that the front end part of the application never performs the authentication to the REST service itself. Instead, only the backend should perform the full authentication since it requires the knowledge of a password secret.
Lease tokens should then be requested as needed by the User facing interface, and only if this interface requires direct interaction with the REST service. Ideally, instead of this a backend proxy should be devised to handle operations, but there are situations like in PWA's style apps where this may not be possible. Lease tokens exist for the sole purpose of handling this scenario.
The following client side code is an example in c# of how refresh tokens can be chained together to keep the service authentication valid. The advantage of switching to refresh tokens when possible is that the username and password do not need to be sent over the wire, reducing security exposure.
This code is provided as just an example. You should modify it to fit your programing language, namespaces and configuration sources.
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
//where your openapi client is
using Rest.Api;
namespace Rest.Utils;
/// <summary>
/// Manages jwt tokens in the background, refreshing the previous one when the old one expires.
/// </summary>
public class JwtTokenManager
{
public string Token { get; set; }
private DateTime tokenExpiration;
private TimeSpan threshold;
private swaggerClient apiClient;
private CancellationTokenSource refreshCancelationSource;
private Task? refreshTask;
private HttpClient http;
public JwtTokenManager(swaggerClient apiClient, HttpClient http, TimeSpan threshold)
{
this.apiClient = apiClient;
this.http = http;
this.refreshCancelationSource = new CancellationTokenSource();
this.threshold = threshold;
this.Token = string.Empty;
}
public async Task<string> Start(string username, string password)
{
//fetch token from full authentication in rest service
var login = await apiClient.AuthLoginAsync(new LoginRequest()
{
Username = username,
Password = password
});
//parse the token to get the expiration date
parseToken(login.Token);
//launch a background task to refresh the token when it expires
refreshTask = Task.Run(tokenRefreshTask, refreshCancelationSource.Token);
return login.Token;
}
public async Task Stop()
{
//Can only stop a task that has been started
if(refreshTask is null) return;
//stop the background task
refreshCancelationSource.Cancel();
//wait for the task to end
await refreshTask;
refreshTask = null;
refreshCancelationSource.Dispose();
refreshCancelationSource = new CancellationTokenSource();
}
private void parseToken(string newToken)
{
//extract expiration date from token
var handler = new JwtSecurityTokenHandler();
var parsedToken = handler.ReadJwtToken(newToken);
tokenExpiration = parsedToken.ValidTo;
Token = newToken;
//set the authentication header on the http client
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", newToken);
}
private async Task tokenRefreshTask()
{
//loop until we signal the process to stop
while (!refreshCancelationSource.Token.IsCancellationRequested)
{
//sleep until the token expires
var refreshTime = tokenExpiration - DateTime.Now - threshold;
if(refreshTime > TimeSpan.Zero)
await Task.Delay(refreshTime , refreshCancelationSource.Token);
//fetch new token
var login = await apiClient.AuthRefreshAsync(null, null);
//parse the token to get the expiration date
parseToken(login.Token);
}
}
}
In the system, the https_redirect
option controls how the application handles HTTPS (Hypertext Transfer Protocol Secure) requests. Ensuring secure communication via HTTPS is critical to protect sensitive information as it travels between your users and the server.
There are three modes available for configuring HTTPS redirection. These modes determine how the application responds when users access the website via HTTP (unsecured) or HTTPS (secured) protocols.
http://
, their browser will automatically switch to https://
.You can configure the https_redirect
option in your application’s appsettings.json
settings. Depending on the infrastructure of your application, this might be done in a configuration file, an environment variable, or an admin panel.
{
"https_redirect": "hsts"
}
The signature for the method generated by this list:
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "DBEditSelectSeveralEntities")]
DBEditResponseSeveralEntities DBEditSelectSeveralEntities(Authentication authentication, LogicalCondition condition, Paging paging, Order order);
The the signature for method generated for the update operation:
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "FormUpdateCompany")]
Response FormUpdateCompany(Authentication authentication, FormRecordCompany record);