A very important part of creating REST APIs is authentication.
We need the ability to sedure API endpoints for different callers.
Some endpoints are unsecured and can be called anonymously while most other endpoints will require some form of authentication.
There are several methods for authentication in use for REST services.
- BASIC
- BEARER
- JWT
- OATUH
- …
in addition to the type of authentication we also want to be able to secure different API routes with different methods and for different API users.
Our REST library implements authentication on a per route basis but also allows definition per controller.
It also allows multiple types of authentication for the same route.
One route could have BASIC authentication as well as BEARER authentication at the same time.
For example a good practice is to require authentication on the controller which automatically forces all routes of a controller to require authentication.
Then, if a route is added that requires anonymous access we can override the authentication on that route only.
so to start we will create a new API controller as follows
Object oAuthSampleAPI is a cszWebAPIController // The psPath property determines the path in the URL for which requests will be handled. Set psPath to "api/v1/authsample" // Use psVerbs to filter based on the HTTP Verbs. Set psVerbs to "*"
Procedure ManualGet Integer hoJson Get Create (RefClass(cJsonObject)) to hoJson Send InitializeJsonType of hoJson jsonTypeObject Send SetMemberValue to hoJson "timestamp" jsonTypeString (ConvertToClient(typeDateTime, CurrentDateTime())) Send SetMemberValue to hoJson "message" jsonTypeString "You can see this message" Send OutputJson hoJson Send Destroy to hoJson End_Procedure Send RegisterPath "GET" "" (RefProc(ManualGet)) Set pbPathScalar to True Set psPathSummary to "Manual API function" End_Object
a very simple controller actually the same as the manual api we built earlier. At this time there is no authentication so we should be able to call the GET method and it will return the HTTP code 200 and the data.
to secure our API we can tell the controller that authentication is required by setting the following property
Set pbAuthRequired to True
running the API now will produce an error 401 and the controller will no longer return any data.
lets see how we can BASIC authentication that will work for the whole controller
first we need to enable BASIC authentication on the controller
Send AddAuth "BASIC"
now that BASIC authentication is enabled we need to validate credentials as they are used.
For BASIC authentication we implement the following event
Function OnValidateAuthBasic String sVerb String sPath tWebAPIPath path String sUser String sPassword Returns Boolean If (sUser="mike" and sPassword="austrianthunder") Begin Function_Return True End End_Function
what if we have a route in our controller that should allow anonymous access. As mentioned earlier it is always safer to force the full controller to authenticate and then override specific functions.
Lets define a sample route with anonymous access
Send RegisterPathAuth "GET" "/anon" (RefProc(ManualGet)) WEPAPI_AUTHMODE_ANONYMOUS Set pbPathScalar to True Set psPathSummary to "Manual API function no auth"
this will register the path /anon with anonymous access.
We can also declare a specific path with a specific type of authentication. For example for BASIC authentication
Send RegisterPathAuth "GET" "/basic" (RefProc(ManualGet)) WEPAPI_AUTHMODE_REQUIRED "BASIC" Set pbPathScalar to True Set psPathSummary to "Manual API basic auth only"
or for both BASIC and BEARER authentication
// register path with BASIC and BEARER auth Send RegisterPathAuth "GET" "/bandb" (RefProc(ManualGet)) WEPAPI_AUTHMODE_REQUIRED "BASIC" Send AddPathAuth "BEARER" Set pbPathScalar to True Set psPathSummary to "Manual API basic and bearer auth"
in our sample application we also added a simple database table to add users to authenticate the web APIs
the code to validate BASIC authentication looks as follows
Function OnValidateAuthBasic String sVerb String sPath tWebAPIPath path String sUser String sPassword Returns Boolean
Clear WEBAUTH
Move “BASIC” to WEBAUTH.TYPE
Move sVerb to WEBAUTH.VERB
Move path.sPath to WEBAUTH.PATH
Move sUser to WEBAUTH.TOKEN
Find EQ WEBAUTH by Index.1
If (Found) Begin
If (WEBAUTH.PASSWORD=sPassword) Function_Return True
End
End_Function