Begin by creating a new folder for your project and initialize a Go module with the following command:
go mod init github.com/vishaaxl/cheershare
Start by setting up a new Go project with the following folder structure:
my-otp-auth-server/ ├── cmd/ │ └── api/ │ └── main.go │ └── user.go │ └── token.go ├── internal/ │ └── data/ │ ├── models.go │ └── user.go │ └── token.go ├── docker-compose.yml ├── go.mod └── Makefile
Next, set up your docker-compose.yml file. This configuration will define the services—PostgreSQL and Redis—that you'll be working with throughout this tutorial.
We will start by configuring the services required for our project. For the backend, we need the following:
Redis: We'll use the redis:6 image. This service will configure a password for secure access, expose port 6379, and enforce password authentication using the --requirepass flag to secure Redis access.
PostgreSQL: We'll use the postgres:13 image. The service will define a default user, password, and database, expose port 5432 for communication, and persist data with a named volume (postgres_data) to ensure durability.
Optional:
// docker-compose.yml services: postgres: image: postgres:13 container_name: postgres environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: mysecretpassword POSTGRES_DB: cheershare ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data redis: image: redis:6 container_name: redis environment: REDIS_PASSWORD: mysecretpassword ports: - "6379:6379" command: ["redis-server", "--requirepass", "mysecretpassword"] volumes: postgres_data:
For routing and handling HTTP requests, we’ll use the github.com/julienschmidt/httprouter package. To install the dependency, run the following command:
go get github.com/julienschmidt/httprouter
Next, create a file at cmd/api/main.go and paste the following code. An explanation for each line is provided in the comments:
// main.go package main import ( "fmt" "log" "net/http" "os" "time" "github.com/julienschmidt/httprouter" ) /* config struct: - Holds application-wide configuration settings such as: - `port`: The port number on which the server will listen. - `env`: The current environment (e.g., "development", "production"). */ type config struct { port int env string } /* applications struct: - Encapsulates the application's dependencies, including: - `config`: The application's configuration settings. - `logger`: A logger instance to handle log messages. */ type applications struct { config config logger *log.Logger } func main() { cfg := &config{ port: 4000, env: "development", } logger := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime) app := &applications{ config: *cfg, logger: logger, } router := httprouter.New() router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, "Welcome to the Go application!") }) /* Initialize the HTTP server - Set the server's address to listen on the specified port. - Assign the router as the handler. - Configure timeouts for idle, read, and write operations. - Set up an error logger to capture server errors. */ srv := &http.Server{ Addr: fmt.Sprintf(":%d", app.config.port), Handler: router, IdleTimeout: time.Minute, ReadTimeout: 10 * time.Second, WriteTimeout: 30 * time.Second, } app.logger.Printf("Starting server on port %d in %s mode", app.config.port, app.config.env) err := srv.ListenAndServe() if err != nil { app.logger.Fatalf("Could not start server: %s", err) } }
Right now, you can test your setup by starting out server using go run ./cmd/api and sending a request to http://localhost:4000, which will return a welcome message. Next, we’ll define three additional routes to implement our core functionality:
/send-otp: This route will handle sending OTPs to users. It will generate a unique OTP, store it in Redis, and deliver it to the user.
/verify-otp: This route will verify the OTP provided by the user. It will check against the value stored in Redis to confirm the user's identity.
/login: This route will handle user login functionality once the OTP is verified and user is successfully created.
But before we continue, we need a way to store user information like phone number and their one-time password for which we need to connect to the services we defined earlier in the docker-compose.yml file.
Before implementing the routes, let’s define two essential helper functions. These functions will handle connections to the Redis and PostgreSQL servers, ensuring that our backend can interact with these services.
Modify the 'config' struct to store information about the services. These functions are pretty self-explanatory.
my-otp-auth-server/ ├── cmd/ │ └── api/ │ └── main.go │ └── user.go │ └── token.go ├── internal/ │ └── data/ │ ├── models.go │ └── user.go │ └── token.go ├── docker-compose.yml ├── go.mod └── Makefile
You can use these functions to establish a connection to the PostgreSQL database and Redis server after starting the services with the docker-compose up -d command.
In the next part, we'll start working on those routes we talked about earlier. This is what your main.go file should look like now.
// docker-compose.yml services: postgres: image: postgres:13 container_name: postgres environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: mysecretpassword POSTGRES_DB: cheershare ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data redis: image: redis:6 container_name: redis environment: REDIS_PASSWORD: mysecretpassword ports: - "6379:6379" command: ["redis-server", "--requirepass", "mysecretpassword"] volumes: postgres_data:
The above is the detailed content of Build an OTP-Based Authentication Server with Go: Part 1. For more information, please follow other related articles on the PHP Chinese website!