DEV Community

Brandon Benefield
Brandon Benefield

Posted on

Securing Microservices with Auth0 Pt. 2 (Resource Service)

This is the second part to a series of posts called Securing Microservices with Auth0. If you missed the previous post, I would suggest you go back and read that post first.

Overview

In this part of the Securing Microservices with Auth0 series, we're going to be creating the Resource Service microservice. The Resource Service will be our applications REST API and will perform CRUD operations on specific users Todos. By this I mean we will be able to:

  • C: Create (POST)
  • R: Read (GET)
  • U: Update (PATCH)
  • D: Delete (DELETE)

At first, this service will be insecure and will not need any sort of authentication. It's important that we see the issues that come with insecure applications both from the developer perspective and the user perspective. After creating our Auth Service, which will be a different microservice in another post in this series, we will then perform authorization on requests being sent to our Resource Service.

You can also go ahead and play around with the code for this post. This branch, bbenefield89/tutorial_pt2, is the UI portion and the insecure RESTful API (Resource Service).

Creating the Resource Service

For this series, I've decided to go with the Spring Framework to create our backend. Microservices are not Java/Spring Framework specific and we can just as easily create our microservices in any language that has the ability to create a web server and make HTTP requests. This means we could potentially create our Resource Service using Express the Node Web Framework and then turn around and create our Auth Service using Django the Python Web Framework. This is one of many of the advantages of going with a microservice architecture when creating your applications.

Enough talk, it's time for action! Let's head over to Spring Initializr where you can quickly create the boilerplate code for your Spring application.

When you land on the Spring Initializr page go ahead and enter in the basic information for your project. As an example, my projects information will look like this:

And my chosen dependencies will be:

Go ahead and click on the green button at the bottom that says Generate the project. This will prompt you to download your project as a zip folder.

Unzip your project, feel free to discard the zipped folder, and let's open up our project in our favorite IDE and get to work.

Inside our Resource Service

Now that we're ready to go, let's find our first file at TodoApp_API/src/main/resources/application.properties and rename that to application.yml as I'm a fan of YAML when it comes to Springs configuration properties.

Inside our application.yml file you'll notice it's empty. Go ahead and place the following text inside:

server:
  port: 8080

It's not much, and to be honest Spring defaults it's PORT to 8080 but I like to be as clear as possible, especially when we have multiple services for the same application.

Creating the Todo Entity

We've already discussed the application and yes, this is going to be yet another todo app but I believe creating something you're familiar with is best when learning about new technology. Might as well focus on the technology instead of the logic.

Create a new package at TodoApp_API/src/main/java/${}/${}/TodoApp_API and name it Entities (TodoApp_API/src/main/java/${}/${}/TodoApp_API/Entities). This package is where we're going to create all of our Entities which are basically just a Java representation of a row in our DB.

Inside the Entities folder, create a new Java file and name it Todo.java and inside of it place the following code (filling in the ${} with your own path). Be sure to read the comments as I'll explain some of the code as we go.

Todo.java

package ${}.${}.TodoApp_API.Entities;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 * This annotation comes from "Lombok" and allows us to forget about writing
 * a lot of boilerplate code like "Constructors/Getters/Setter"
 */
@Data
// Creates this class as a Bean to be picked up by Spring
@Entity
public class Todo {

    // Lets JPA know this is the unique identifier for our DB
    @Id
    // Sets the value that should be automatically generated for our ID in the DB
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    // We'll use the users' email address to find a user's todos
    private String userEmailAddress;

    /**
     * Notice we don't have to write anything else
     * Lombok will take care of this for us
     */

}

Creating the TodoRepository "Repository"

The Repository for an Entity is going to be an interface that will extend another interface that comes with a ton of helpful methods to perform all of our CRUD operations.

Create another package named TodoRepositories and place it at TodoApp_API/src/main/java/${}/${}/TodoApp_API/Repositories. Inside create a new file named TodoRepository.java and inside place the following code:

TodoRepository.java

package ${}.${}.TodoApp_API.Repositories;

import ${}.${}.TodoApp_API.Entities.Todo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * Sets this interface up to be found by Spring
 * Later on we'll be taking advantage of the @Autowired annotation where this interface will then become a
 * concrete class
 */
@Repository
/**
 * Our repository interface needs to extend the JpaRepository interface and pass along two arguments
 * 1. The Entity class that this repository is responsible for
 * 2. The id data type we chose for the Entity this Repository is responsble for
 * In this example, we've chosen to create our id as a Long data type
 */
public interface TodoRepository extends JpaRepository<Todo, Long> {

    /**
     * This is a custom method we'll be using to get a list of todos depending on the users email address
     * JPA supports a type of DSL where we can create methods that relate to an Entity by using keywords
     * 1. "findAll": returns a List of Todo
     * 2. "By": This signifies that we are going to be giving something specific to look for in the DB
     * 3. "UserEmailAddress": Find a Todo that contains the correct "userEmailAddress" in the DB
     */
    public List<Todo> findAllByUserEmailAddress(String userEmailAddress);

    /**
     * Another custom method. This method will take the ID of a Todo and the users email address to return a
     * single Todo
     */
    public Todo findByIdAndUserEmailAddress(Long todoId, String userEmailAddress);

    /**
     * This custom method will delete a single Todo depending on the ID and the userEmailAddress
     */
    public void deleteByIdAndUserEmailAddress(Long todoId, String userEmailAddress);

}


That's it for our Repository. We've only added a few methods but JpaRepository will still give us access to a lot more of the inner methods that we haven't defined.

Creating the TodoService "Service"

The idea behind a Service in this context is to bridge the gap between a Controller and a Repository. This is also where you will write your business logic. Splitting up your code like this keeps things small and typically easier to reason about.

Go ahead and create another package named Services and place it at TodoApp_API/src/main/java/${}/${}/TodoApp_API/Services. Inside, create a file named TodoService.java.

TodoService.java

package ${}.${}.TodoApp_API.Services;

import ${}.${}.TodoApp_API.Entities.Todo;
import ${}.${}.TodoApp_API.Repositories.TodoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Lets Spring know to pick this up at runtime
 * You've probably noticed that so far we haven't really told Spring when to use any of our classes and that's
 * because of "Component Scanning". To learn more about the Component Scanning go to the following URL
 * https://www.baeldung.com/spring-component-scanning
 */
@Service
public class TodoService {

    TodoRepository todoRepository;

    /**
     * @Autowired annotation sets this constructor to be called when booting our application and will automagically
     * inject any dependencies that we specify in the arguments
     * This is also known as "Dependency Injection" and is one of the more attractive aspects of the Spring Framework
     */
    @Autowired
    public TodoService(TodoRepository todoRepository) {
        this.todoRepository = todoRepository;
    }

    // Returns a List of all of a users Todos
    public List<Todo> findAllByUserEmailAddress(String userEmailAddress) {
        return todoRepository.findAllByUserEmailAddress(userEmailAddress);
    }

    // Return a single Todo
    public Todo findByIdAndUserEmailAddress(Long todoId, String userEmailAddress) {
        return todoRepository.findByIdAndUserEmailAddress(todoId, userEmailAddress);
    }

    // Create/Update a new Todo and returns that Todo
    public Todo save(String userEmailAddress, Todo todo) {
        todo.setUserEmailAddress(userEmailAddress);
        return todoRepository.save(todo);
    }

    // Delete a Todo
    public void deleteByIdAndUserEmailAddress(Long todoId, String userEmailAddress) {
        todoRepository.deleteByIdAndUserEmailAddress(todoId, userEmailAddress);
    }

}

Creating the TodoController "Rest Controller"

Okay, we're almost finished with our first pass on our Resource Service. We just need to create the Controller that will determine our services' URL endpoints.

Create your final package named Controllers and place it at TodoApp_API/src/main/java/${}/${}/TodoApp_API/Controllers. Inside, create yet another file and name it TodoController.java and place the following code inside.

TodoController.java

package io.github.bbenefield89.TodoApp_API.Controllers;

import io.github.bbenefield89.TodoApp_API.Entities.Todo;
import io.github.bbenefield89.TodoApp_API.Services.TodoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/todos")
public class TodoController {

    private TodoService todoService;

    @Autowired
    public TodoController(TodoService todoService) {
        this.todoService = todoService;
    }

    // Returns a List of Todos
    @GetMapping("/{userEmailAddress}")
    public List<Todo> findAllByUserEmailAddress(@PathVariable String userEmailAddress) {
        return todoService.findAllByUserEmailAddress(userEmailAddress);
    }

    // Returns a single Todo
    @GetMapping("/{userEmailAddress}/{todoId}")
    public Todo findByIdAndUserEmailAddress(@PathVariable String userEmailAddress, @PathVariable Long todoId) {
        return todoService.findByIdAndUserEmailAddress(todoId, userEmailAddress);
    }

    // Creates a new Todo
    @PostMapping("/{userEmailAddress}")
    public Todo save(@PathVariable String userEmailAddress, @RequestBody Todo todo) {
        return todoService.save(userEmailAddress, todo);
    }

    // Deletes a single Todo
    @DeleteMapping("/{userEmailAddress}/{todoId}")
    public void deleteByIdAndUserEmailAddress(@PathVariable String userEmailAddress, @PathVariable Long todoId) {
        todoService.deleteByIdAndUserEmailAddress(todoId, userEmailAddress);
    }

}

Manually testing our endpoints

Now that we've written our endpoints it's time we test them to make sure everything works. I would suggest downloading Postman for API testing.

Let's go ahead and start making some HTTP requests.

POST localhost:8080/api/todos/user@gmail.com (Create Todo)

Example Request

{
    "title": "Get a haircut",
    "userEmailAddress": "user@gmail.com"
}

Example Response

{
    "id": 1,
    "title": "Get a haircut",
    "userEmailAddress": "user@gmail.com"
}

GET localhost:8080/api/todos/user@gmail.com (Get All Todos)

Example Request

Nothing required

Example Response

[
    {
        "id": 1,
        "title": "Get a haircut",
        "userEmailAddress": "user@gmail.com"
    }
]

GET localhost:8080/api/todos/user@gmail.com/1 (Get a Single Todo)

Example Request

Nothing required

Example Response

{
    "id": 1,
    "title": "Get a haircut",
    "userEmailAddress": "user@gmail.com"
}

DELETE localhost:8080/api/todos/user@gmail.com/1 (DELETE a Single Todo)

Example Request

Nothing required

Example Response

Nothing returned

Great, everything works! The only problem now is that our endpoints aren't secured (to be fair we don't really have any users either). This means, you as the user hacker_man@gmail.com could easily access my data and vice versa.

Conclusion

In this post, you didn't learn much about Spring or Auth0 but you did learn about creating RESTful endpoints which is an important step to the process. Not to mention, you now see how easy it is for insecure endpoints to be accessed by the wrong people.

In the next section of this series (link coming soon), you'll get an introduction on how to create a very simple Auth Service that uses:

  • Spring Security: Prevent access to users that are not authed

  • Prehandle: A method to intercept requests to endpoints that we can use to run logic before all requests (the secret sauce of our auth)

Top comments (0)