diff --git a/.gitignore b/.gitignore index 76b5cf8..d34b3e2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ TODO.md logs.txt .idea/ secret.md -app.env \ No newline at end of file +tmp/ \ No newline at end of file diff --git a/app.env b/app.env new file mode 100644 index 0000000..cb1c622 --- /dev/null +++ b/app.env @@ -0,0 +1,19 @@ +POSTGRES_HOST=127.0.0.1 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=password123 +POSTGRES_DB=golang-gorm +POSTGRES_PORT=6500 + +PORT=8000 +CLIENT_ORIGIN=http://localhost:3000 + +ACCESS_TOKEN_PRIVATE_KEY=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCUEFJQkFBSkJBTzVIKytVM0xrWC91SlRvRHhWN01CUURXSTdGU0l0VXNjbGFFKzlaUUg5Q2VpOGIxcUVmCnJxR0hSVDVWUis4c3UxVWtCUVpZTER3MnN3RTVWbjg5c0ZVQ0F3RUFBUUpCQUw4ZjRBMUlDSWEvQ2ZmdWR3TGMKNzRCdCtwOXg0TEZaZXMwdHdtV3Vha3hub3NaV0w4eVpSTUJpRmI4a25VL0hwb3piTnNxMmN1ZU9wKzVWdGRXNApiTlVDSVFENm9JdWxqcHdrZTFGY1VPaldnaXRQSjNnbFBma3NHVFBhdFYwYnJJVVI5d0loQVBOanJ1enB4ckhsCkUxRmJxeGtUNFZ5bWhCOU1HazU0Wk1jWnVjSmZOcjBUQWlFQWhML3UxOVZPdlVBWVd6Wjc3Y3JxMTdWSFBTcXoKUlhsZjd2TnJpdEg1ZGdjQ0lRRHR5QmFPdUxuNDlIOFIvZ2ZEZ1V1cjg3YWl5UHZ1YStxeEpXMzQrb0tFNXdJZwpQbG1KYXZsbW9jUG4rTkVRdGhLcTZuZFVYRGpXTTlTbktQQTVlUDZSUEs0PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQ== +ACCESS_TOKEN_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZ3d0RRWUpLb1pJaHZjTkFRRUJCUUFEU3dBd1NBSkJBTzVIKytVM0xrWC91SlRvRHhWN01CUURXSTdGU0l0VQpzY2xhRSs5WlFIOUNlaThiMXFFZnJxR0hSVDVWUis4c3UxVWtCUVpZTER3MnN3RTVWbjg5c0ZVQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ== +ACCESS_TOKEN_EXPIRED_IN=15m +ACCESS_TOKEN_MAXAGE=15 + + +REFRESH_TOKEN_PRIVATE_KEY=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCT1FJQkFBSkJBSWFJcXZXeldCSndnYjR1SEhFQ01RdHFZMTI5b2F5RzVZMGlGcG51a0J1VHpRZVlQWkE4Cmx4OC9lTUh3Rys1MlJGR3VxMmE2N084d2s3TDR5dnY5dVY4Q0F3RUFBUUpBRUZ6aEJqOUk3LzAxR285N01CZUgKSlk5TUJLUEMzVHdQQVdwcSswL3p3UmE2ZkZtbXQ5NXNrN21qT3czRzNEZ3M5T2RTeWdsbTlVdndNWXh6SXFERAplUUloQVA5UStrMTBQbGxNd2ZJbDZtdjdTMFRYOGJDUlRaZVI1ZFZZb3FTeW40YmpBaUVBaHVUa2JtZ1NobFlZCnRyclNWZjN0QWZJcWNVUjZ3aDdMOXR5MVlvalZVRlVDSUhzOENlVHkwOWxrbkVTV0dvV09ZUEZVemhyc3Q2Z08KU3dKa2F2VFdKdndEQWlBdWhnVU8yeEFBaXZNdEdwUHVtb3hDam8zNjBMNXg4d012bWdGcEFYNW9uUUlnQzEvSwpNWG1heWtsaFRDeWtXRnpHMHBMWVdkNGRGdTI5M1M2ZUxJUlNIS009Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0t +REFRESH_TOKEN_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZ3d0RRWUpLb1pJaHZjTkFRRUJCUUFEU3dBd1NBSkJBSWFJcXZXeldCSndnYjR1SEhFQ01RdHFZMTI5b2F5Rwo1WTBpRnBudWtCdVR6UWVZUFpBOGx4OC9lTUh3Rys1MlJGR3VxMmE2N084d2s3TDR5dnY5dVY4Q0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ== +REFRESH_TOKEN_EXPIRED_IN=60m +REFRESH_TOKEN_MAXAGE=60 \ No newline at end of file diff --git a/controllers/auth.controller.go b/controllers/auth.controller.go index df7ee56..6da2351 100644 --- a/controllers/auth.controller.go +++ b/controllers/auth.controller.go @@ -56,7 +56,10 @@ func (ac *AuthController) SignUpUser(ctx *gin.Context) { result := ac.DB.Create(&newUser) - if result.Error != nil { + if result.Error != nil && strings.Contains(result.Error.Error(), "duplicate key value violates unique") { + ctx.JSON(http.StatusConflict, gin.H{"status": "fail", "message": "User with that email already exists"}) + return + } else if result.Error != nil { ctx.JSON(http.StatusBadGateway, gin.H{"status": "error", "message": "Something bad happened"}) return } diff --git a/migrate/migrate.go b/migrate/migrate.go index 86e4c3e..72081ce 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -18,6 +18,7 @@ func init() { } func main() { + initializers.DB.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"") initializers.DB.AutoMigrate(&models.User{}) fmt.Println("👍 Migration complete") } diff --git a/models/user.model.go b/models/user.model.go index 00665a5..b771f93 100644 --- a/models/user.model.go +++ b/models/user.model.go @@ -8,19 +8,15 @@ import ( type User struct { ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4();primary_key"` - Name string `gorm:"type:varchar(255)"` - Email string `gorm:"uniqueIndex"` - Password string - Role string `gorm:"type:varchar(255)"` - Provider string - Photo string - Verified bool - CreatedAt time.Time - UpdatedAt time.Time -} - -func (User) UsersTable() string { - return "users" + Name string `gorm:"type:varchar(255);not null"` + Email string `gorm:"uniqueIndex;not null"` + Password string `gorm:"not null"` + Role string `gorm:"type:varchar(255);not null"` + Provider string `gorm:"not null"` + Photo string `gorm:"not null"` + Verified bool `gorm:"not null"` + CreatedAt time.Time `gorm:"not null"` + UpdatedAt time.Time `gorm:"not null"` } type SignUpInput struct { diff --git a/readMe.md b/readMe.md index c1c43d0..fbfeb31 100644 --- a/readMe.md +++ b/readMe.md @@ -1,5 +1,52 @@ -# Build Golang RESTful API with Gorm, Gin and Postgres +# API with Golang + GORM + PostgreSQL: Access & Refresh Tokens + +This article will teach you how to secure a Golang application with access and refresh tokens using GORM, Postgres, Docker, and Docker-compose. Also, you will learn how to use the RS256 algorithm with private and public keys to sign the tokens. + +![API with Golang + GORM + PostgreSQL: Access & Refresh Tokens](https://codevoweb.com/wp-content/uploads/2022/08/API-with-Golang-GORM-PostgreSQL-Access-Refresh-Tokens.webp) + +## Topics Covered + +- Golang, Gin & GORM JWT Authentication Overview +- JWT Authentication Example with Golang and GORM +- Generate the Private and Public Keys +- Load and Validate the Environment Variables +- Create the Database Models with GORM +- Run the Database Migration with GORM +- Hash and Verify the Passwords with Bcrypt +- Sign and Verify the RS256 JSON Web Tokens + - Function to Generate the Tokens + - Function to Verify the Tokens +- Create the Authentication Route Controllers + - Register User Controller + - Login User Controller + - Refresh Access Token Controller + - Logout Controller +- Create a Middleware Guard +- Create a User Controller + - Authentication Routes + - User Routes +- Add the Routes to the Gin Middleware Stack + +Read the entire article here: [https://codevoweb.com/golang-gorm-postgresql-user-registration-with-refresh-tokens](https://codevoweb.com/golang-gorm-postgresql-user-registration-with-refresh-tokens) + +Articles in this series: ### 1. How to Setup Golang GORM RESTful API Project with Postgres [How to Setup Golang GORM RESTful API Project with Postgres](https://codevoweb.com/setup-golang-gorm-restful-api-project-with-postgres/) + +### 2. API with Golang + GORM + PostgreSQL: Access & Refresh Tokens + +[API with Golang + GORM + PostgreSQL: Access & Refresh Tokens](https://codevoweb.com/golang-gorm-postgresql-user-registration-with-refresh-tokens) + +### 3. Golang and GORM - User Registration and Email Verification + +[Golang and GORM - User Registration and Email Verification](https://codevoweb.com/golang-and-gorm-user-registration-email-verification) + +### 4. Forgot/Reset Passwords in Golang with SMTP HTML Email + +[Forgot/Reset Passwords in Golang with SMTP HTML Email](https://codevoweb.com/forgot-reset-passwords-in-golang-with-html-email) + +### 5. Build a RESTful CRUD API with Golang + +[Build a RESTful CRUD API with Golang](https://codevoweb.com/build-restful-crud-api-with-golang)