Intro
In order to talk about API Authentication we need to understand the difference between Authentication and Authorization. API Authentication and Authorization are two distinct but closely related concepts crucial for securing APIs.
- Authentication is the process of verifying the identity of a user or application making a request, ensuring that they are who they claim to be. An example would be authenticating a user's login credentials.
- Authorization, on the other hand, occurs after authentication and involves determining what resources or actions an authenticated user is allowed to access or perform. This process ensures that even if a user is authenticated, they can only access the data or functionality they are permitted to.
When using APIs we are usually talking about Authentication. We are going to verify that this user has "Authentication" to access this APIs resources.
Discussion Resources
For the purpose of this discussion, we have created some relevant resources. First, we are going to be using an API written by Dr. Angela Yu. It's called Secrets and can be found @ https://secrets-api.appbrewery.com/. This API essentially lets users add "Secret" things that they do when they think no one is watching, to the API Database.
Remember, the purpose of this discussion is API Authentication which means that we are not going to be dissecting this API and talking about how to manipulate every aspect of it. We are only discussing the Authentication aspect here. To that end, we are going to be writing code that will allow us to work with this API to allow us to demonstrate some of the different methods of Authentication.
This process is all "server" driven, so you must have your "localhost" up and running to view the results. We should be familiar with how to get our server up and running. As a refresher, you can access the "Server Setup" discussion here.
The completed files can be downloaded here:
basicApiAuth.zip
We are going to be using the following technologies and middleware on this project:
- node.js
- express
- ejs
- axios
We should be familiar with node.js and express as they are the basics of the client-server operations we have been utilizing for some time now.
Axios is middleware that was designed to facilitate an improved method of communicating with APIs through get and post requests. ejs allows us to embed JavaScript directly into html files with the .ejs extension, which are usually located in a views directory.
And lastly, we used Postman (which can be downloaded here), to create all of our credentials to access to API. We did this separately as it is outside of the scope of this discussion. All of the information on creating your API Authentication credentials, can be found with their API documentation.
Authentication Types
There are many ways to Authorize a user's access. Here are a couple of articles discussing the concept:
In this discussion we are going to be detailing four different types of Authentication:
- No Authentication
- Basic Authentication (username / password)
- API Key Authentication
- Token Based Authentication
Basic Setup
For all of our examples below, we have set up some basic variables that will be used:
- JavaScript
- const user = "yourUserName";
- const pswd = "yourPassword";
- const apiKey = "yourAPIkey";
- const bearToken = "yourBearToken";
- const config = {
-
- headers: { Authorization: `Bearer ${bearToken}` },
- };
In our index.ejs file that displays our page, we have a series of four buttons that you "click" to request our different get request's. They each have an inline Js "click event" to establish which get request is processed. We will list all four, but remember, each click event is unique to each button.
- index.ejs
- <button type="submit" onclick="window.location.href='/noAuth'</button>
- <button onclick="window.location.href='/basicAuth''</button>
- <button onclick="window.location.href='/apiKey''</button>
- <button onclick="window.location.href='/bearerToken''</button>
Using this method allows our server file (index.js) to know which button was "clicked" and which get request to process.
The Authentication Methods
No Authentication
No Authentication is the simplest form of API access. It allows anyone to access the API without any credentials. This is often used for public APIs where data is not sensitive or restricted. The code to submit our get request is simple considering no Authentication is required and will become the Basic Get Request layout for all of our subsequent get request's.
In this example, our get request will return a random secret from the API. Here is the code:
- JavaScript
- app.get("/noAuth", async (req, res) => {
- try {
- const result = await axios.get("https://secrets-api.appbrewery.com/random")
- res.render("index.ejs", { content: JSON.stringify(result.data) });
- } catch (error) {
- res.render("index.ejs", { content: error.message });
- }
- });
This is fairly simple code, which is exactly why we're using the middleware axios. If we were just using vanilla JavaScript and not using axios, we would have many, many more lines of code to accomplish the same result.
Let's break this code snippet down:
- - We are processing a get request for /noAuth because that was the button "click event" that the user selected (in index.ejs).
- - We are using an async function so that we can await the result of or request.
- ~ we need to await our request so that the code does not move on before our get request is completed.
- ~ we can only use await inside an async function.
- - We are using the try/catch method to process our request. If the request is successful, we render the output, if not, we render the error.
- - The address we are using (https://secrets-api.appbrewery.com/random) comes from the API Documentation and will return one random secret.
And finally, we are using stringify to send our result back to index.ejs. We use this method because stringify converts our data objects into strings, which allows for easier storage and transmission of the data. And because we are using stringify(), we will have to parse() this output in our index.ejs file to return the data to it's original "object" form, for output processing.
This code outlines the "basics" for all of our API get request's. All of the rest of our requests will follow the same overall format with minor changes reflecting which "button" the user selects in index.ejs, the actual get("requestAddress") and of course, the unique Authentication method used by each get request.
Basic Authentication
With this API, you could request a list of every "secret" in the database, and the API would normally return ten items at a time. With this particular get request, we are going to be requesting all of the secrets for page 2, so this should give us ten secrets.
Since the "Basic Authentication" for this API requires the use of a username and password we're going to need to modify our "Basic get request" a little. Here is the code:
- JavaScript
- app.get("/basicAuth", async (req, res) => {
- try {
- const result = await axios.get("https://secrets-api.appbrewery.com/all?page=2", {
- auth: {
- username: yourUserName,
- password: yourPassword,
- }
- });
- res.render("index.ejs", { content: JSON.stringify(result.data) });
- } catch (error) {
- res.render("index.ejs", { content: error.message });
- }
- });
Now this is essentially the same basic code as above with the following changes:
- - The app.get request has changed to /basicAuth to reflect the button "click event" that was initiated from index.ejs.
- - The axios.get address has changed to retrieve /all of the secrets from page=2 of the total "Secrets" list.
- - And we've added our authentication credentials that this API requires for the type of data we want to retrieve.
The get request wil pass our authentication credentials in the header as base64 encoded data. This is not considered a "secure" way of transferring this information. If someone intercepts the header, they could use any of a number of different base64 decryption apps available today to decode this data and have full access to it.
While this code works, there are much better ways to transmit our "sensitive" Authentication credentials, such as OAuth, or other modern authentication protocols.
API Key Authentication
This API has set up a "Scoring" method to basically rate user's "Secrets". This rating is called an "emScore". With our code here we are requesting all of the "Secrets" with a "score" of 5 or greater.
- JavaScript
- app.get("/apiKey", async (req, res) => {
- try {
- const result = await axios.get("https://secrets-api.appbrewery.com/filter?score=5&apiKey=yourApiKey")
- res.render("index.ejs", { content: JSON.stringify(result.data) });
- } catch (error) {
- res.render("index.ejs", { content: error.message });
- }
- });
And once again, this is essentially the same basic code as above with the following changes:
- - The app.get request has changed to /apiKey to reflect the button "click event" that was initiated from index.ejs.
- - The axios.get address has changed to retrieve "/filter?score=5". (retrieve all secrets with an emScore of 5 or greater)
- - And notice how we've added our API KEY authentication credentials to the end or our query string for our axios.get request (&apiKey=yourApiKey).
Token Based Authentication
With this API, every "Secret" is given a specific ID number when they are input into the database. With this final request, we are going to request secret number 42.
With Token Based Authentication, we're going to do something a little different. We're adding a variable to our server file (index.js) that will contain our Authentication credentials.
- JavaScript
- const config = {
- headers: { Authorization: `Bearer ${yourBearerToken}` },
- };
This just simplifies the code for our axios.get request. *Note: if we wanted to, we could also create separate variables for all of our Authentication credentials. And once again, the rest of our get request is pretty much the same as the others, with minor changes.
- JavaScript
- app.get("/bearerToken", async (req, res) => {
- try {
- const result = await axios.get("https://secrets-api.appbrewery.com/secrets/42", config)
- res.render("index.ejs", { content: JSON.stringify(result.data) });
- } catch (error) {
- res.render("index.ejs", { content: error.message });
- }
- });
And the changes here are:
- - The app.get request has changed to /bearerToken to reflect the button "click event" that was initiated from index.ejs.
- - The axios.get address has changed to retrieve "/secrets/42. (retrieve secret number 42)
- - And notice how we've added our bearerToken config, which is our Authentication credentials to our query string for our axios.get request.
Conclusion
This covers the basics of the four main authentication methods commonly used with APIs:
- No Authentication
- Basic Authentication
- API Key Authentication
- Token Based Authentication
Each method has its own use cases, advantages, and security considerations. When working with APIs, always choose the authentication method that best fits the sensitivity of your data and the requirements of your application. For more advanced scenarios, consider exploring OAuth or other modern authentication protocols.