FreePascal REST API’s — Authenticating requests with Basic Auth
Before your REST API performs actions for a user, it’s a good idea to authenticate the request.
There are several ways to authenticate API requests. The one we’ll be talking about today is called Basic Auth.
Basic Auth is a tried-and-true method for passing authentication information with a request used for both websites and REST API’s.
It’s very easy to use, it’s just a Base-64 encoded value in the Authorization header.
Just remember that while the authentication payload is encoded, it’s not encrypted.
Always use HTTPS/SSL if at all possible to protect against MITM attacks.
Using Basic Auth is very easy.
In the Authorization
header you need to place the value Basic username:password
and the username:password
part needs to be base64 encoded.
Here’s an example. let’s say you have the username bob
and a password bobiscool
You base-64 encode the value bob:bobiscool
which gives you Ym9iOmJvYmlzY29vbA==
Now add that to the Authorization header, specifying that we’re using Basic
authentication.
Authorization: Basic Ym9iOmJvYmlzY29vbA==
To encode and decode base64 strings in FreePascal/Lazarus, use the SimpleBaseLib, you can find it in the OPM (Online Package Manager) under Meny -> Package -> Online Package Manager
Add base64
to your uses
section and you’re ready to go.
The process for implementing Basic Auth in an API is straight forward
- Check if there’s a value in the Authorization header
- Make sure the first word says
Basic
- Base-64 Decode the second word
- Grab the username and password from the decoded value
- Do whatever validation your app needs
I’ve created a template for Basic Auth REST endpoints, you can find it on GitHub.
Here’s the template for Basic Authentication
{
Where https://github.com/MFernstrom/rest-api-templates
What Basic Auth template
Who Marcus Fernström
License Apache 2.0
Version 1.0
}program BasicAuthTemplate;{$mode objfpc}{$H+}uses
{$IFDEF UNIX}cthreads,
cmem, {$ENDIF}
SysUtils,
strutils,
fphttpapp,
httpdefs,
httproute,
fpjson,
base64; procedure validateRequest(aRequest: TRequest);
var
headerValue, b64decoded, username, password: string;
begin
headerValue := aRequest.Authorization; if length(headerValue) = 0 then
raise Exception.Create('This endpoint requires authentication'); if ExtractWord(1, headerValue, [' ']) <> 'Basic' then
raise Exception.Create('Only Basic Authentication is supported'); b64decoded := DecodeStringBase64(ExtractWord(2, headerValue, [' ']));
username := ExtractWord(1, b64decoded, [':']);
password := ExtractWord(2, b64decoded, [':']); // Replace this with your own logic
if (username <> 'marcus') or (password <> '112233') then
raise Exception.Create('Invalid API credentials');
end; procedure jsonResponse(aResponse: TResponse; JSON: TJSONObject; httpCode: integer);
begin
aResponse.Content := JSON.AsJSON;
aResponse.Code := httpCode;
aResponse.ContentType := 'application/json';
aResponse.ContentLength := length(aResponse.Content);
aResponse.SendContent;
end; procedure apiEndpoint(aRequest: TRequest; aResponse: TResponse);
var
JSON: TJSONObject;
httpCode: integer;
begin
JSON := TJSONObject.Create;
try
try
validateRequest(aRequest);
JSON.Add('success', True);
JSON.Add('time', DateToStr(now));
httpCode := 200;
except
on E: Exception do
begin
JSON.Add('success', False);
JSON.Add('reason', E.message);
httpCode := 401;
end;
end;
jsonResponse(aResponse, JSON, httpCode); finally
JSON.Free;
end;
end;begin
HTTPRouter.RegisterRoute('/api', @apiEndpoint);
Application.Port := 9080;
Application.Threaded := True;
Application.Initialize;
WriteLn(format('API is ready at http://localhost:%d/', [Application.Port]));
Application.Run;
end.
Go ahead and copy the code into the Lazarus IDE, Medium isn’t the greatest place for reading source code.
We have three procedures in this template.
jsonResponse
validateRequest
apiEndpoint
jsonResponse
This procedure has a single purpose, to take a JSONObject and create a response to the request. It’s not all that helpful when we only have one endpoint, but when you have several endpoints it’s a good way to stay DRY (Don’t Repeat Yourself).
validateRequest
The validateRequest
procedure is where we do the validation of the the authorization credentials.
We use a simple pattern of throwing errors with custom messages if anything goes wrong or the validation fails.
We check to make sure the authorization header is present, has a value, and is set to Basic
before we check the username and password at the end
if (username <> 'marcus') or (password <> '112233') then
raise Exception.Create('Invalid API credentials');
This is where you implement your check to see if the credentials are valid or not.
Remember to hash the password when storing it.
apiEndpoint
This procedure gets invoked when a user makes a request for the /api
path, set in HTTPRouter.RegisterRoute
We wrap the invocation of validateRequest
in a try/except and if there’s an error we respond with a 401 (HTTP code for an unauthorized request).
If there’s no error, we know that validation passed so we carry on with the request.
In this template endpoint we add the current time to the TJSONObject and respond with http code 200 (Ok).
Output
Using the Insomnia desktop tool it’s really easy to test endpoint.
Here I’m hitting it without passing any authorization details
It’s a little hard to read. Let’s just look at the responses on the right-hand side.
If we don’t pass in any authorization details at all, we see this
And if we pass credentials but they’re incorrect, our endpoint lets us know.
And finally, if we pass in the correct credentials, we get a 200
response with the current time.
That’s all there is to using Basic Auth in FreePascal. Feel free to use the template as a starting point.
If you like my articles, give it a Clap and a Share, it really helps me out.
If you have questions, ideas, or feedback, connect with me on LinkedIn.