8 changed files with 184 additions and 0 deletions
@ -0,0 +1,7 @@ |
|||
db_name = gocontacts |
|||
db_pass = password |
|||
db_user = postgres |
|||
db_type = postgres |
|||
db_host = localhost |
|||
db_port = 5434 |
|||
token_password = thisIsTheJwtSecretPassword //Do not commit to git! |
|||
@ -0,0 +1,79 @@ |
|||
package app |
|||
|
|||
import ( |
|||
"net/http" |
|||
u "../utils" |
|||
"strings" |
|||
"../models" |
|||
jwt "github.com/dgrijalva/jwt-go" |
|||
"os" |
|||
"context" |
|||
"fmt" |
|||
) |
|||
|
|||
var JwtAuthentication = func(next http.Handler) http.Handler { |
|||
|
|||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|||
|
|||
notAuth := []string{"/api/user/new", "/api/user/login"} //List of endpoints that doesn't require auth
|
|||
requestPath := r.URL.Path //current request path
|
|||
|
|||
//check if request does not need authentication, serve the request if it doesn't need it
|
|||
for _, value := range notAuth { |
|||
|
|||
if value == requestPath { |
|||
next.ServeHTTP(w, r) |
|||
return |
|||
} |
|||
} |
|||
|
|||
response := make(map[string] interface{}) |
|||
tokenHeader := r.Header.Get("Authorization") //Grab the token from the header
|
|||
|
|||
if tokenHeader == "" { //Token is missing, returns with error code 403 Unauthorized
|
|||
response = u.Message(false, "Missing auth token") |
|||
w.WriteHeader(http.StatusForbidden) |
|||
w.Header().Add("Content-Type", "application/json") |
|||
u.Respond(w, response) |
|||
return |
|||
} |
|||
|
|||
splitted := strings.Split(tokenHeader, " ") //The token normally comes in format `Bearer {token-body}`, we check if the retrieved token matched this requirement
|
|||
if len(splitted) != 2 { |
|||
response = u.Message(false, "Invalid/Malformed auth token") |
|||
w.WriteHeader(http.StatusForbidden) |
|||
w.Header().Add("Content-Type", "application/json") |
|||
u.Respond(w, response) |
|||
return |
|||
} |
|||
|
|||
tokenPart := splitted[1] //Grab the token part, what we are truly interested in
|
|||
tk := &models.Token{} |
|||
|
|||
token, err := jwt.ParseWithClaims(tokenPart, tk, func(token *jwt.Token) (interface{}, error) { |
|||
return []byte(os.Getenv("token_password")), nil |
|||
}) |
|||
|
|||
if err != nil { //Malformed token, returns with http code 403 as usual
|
|||
response = u.Message(false, "Malformed authentication token") |
|||
w.WriteHeader(http.StatusForbidden) |
|||
w.Header().Add("Content-Type", "application/json") |
|||
u.Respond(w, response) |
|||
return |
|||
} |
|||
|
|||
if !token.Valid { //Token is invalid, maybe not signed on this server
|
|||
response = u.Message(false, "Token is not valid.") |
|||
w.WriteHeader(http.StatusForbidden) |
|||
w.Header().Add("Content-Type", "application/json") |
|||
u.Respond(w, response) |
|||
return |
|||
} |
|||
|
|||
//Everything went well, proceed with the request and set the caller to the user retrieved from the parsed token
|
|||
fmt.Sprintf("User %", tk.Username) //Useful for monitoring
|
|||
ctx := context.WithValue(r.Context(), "user", tk.UserId) |
|||
r = r.WithContext(ctx) |
|||
next.ServeHTTP(w, r) //proceed in the middleware chain!
|
|||
}); |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
package app |
|||
|
|||
import ( |
|||
"net/http" |
|||
u "../utils" |
|||
) |
|||
|
|||
var NotFoundHandler = func(next http.Handler) http.Handler { |
|||
|
|||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|||
w.WriteHeader(http.StatusNotFound) |
|||
u.Respond(w, u.Message(false, "This resources was not found on our server")) |
|||
next.ServeHTTP(w, r) |
|||
}) |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"github.com/gorilla/mux" |
|||
"./app" |
|||
"os" |
|||
"fmt" |
|||
"net/http" |
|||
) |
|||
|
|||
func main() { |
|||
|
|||
router := mux.NewRouter() |
|||
router.Use(app.JwtAuthentication) //attach JWT auth middleware
|
|||
|
|||
port := os.Getenv("PORT") //Get port from .env file, we did not specify any port so this should return an empty string when tested locally
|
|||
if port == "" { |
|||
port = "8000" //localhost
|
|||
} |
|||
|
|||
fmt.Println(port) |
|||
|
|||
err := http.ListenAndServe(":" + port, router) //Launch the app, visit localhost:8000/api
|
|||
if err != nil { |
|||
fmt.Print(err) |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
package models |
|||
|
|||
import ( |
|||
_ "github.com/jinzhu/gorm/dialects/postgres" |
|||
"github.com/jinzhu/gorm" |
|||
"os" |
|||
"github.com/joho/godotenv" |
|||
"fmt" |
|||
) |
|||
|
|||
var db *gorm.DB //database
|
|||
|
|||
func init() { |
|||
|
|||
e := godotenv.Load() //Load .env file
|
|||
if e != nil { |
|||
fmt.Print(e) |
|||
} |
|||
|
|||
username := os.Getenv("db_user") |
|||
password := os.Getenv("db_pass") |
|||
dbName := os.Getenv("db_name") |
|||
dbHost := os.Getenv("db_host") |
|||
|
|||
|
|||
dbUri := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=disable password=%s", dbHost, username, dbName, password) //Build connection string
|
|||
fmt.Println(dbUri) |
|||
|
|||
conn, err := gorm.Open("postgres", dbUri) |
|||
if err != nil { |
|||
fmt.Print(err) |
|||
} |
|||
|
|||
db = conn |
|||
db.Debug().AutoMigrate(&Account{}, &Contact{}) //Database migration
|
|||
} |
|||
|
|||
//returns a handle to the DB object
|
|||
func GetDB() *gorm.DB { |
|||
return db |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
package utils |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"net/http" |
|||
) |
|||
|
|||
func Message(status bool, message string) (map[string]interface{}) { |
|||
return map[string]interface{} {"status" : status, "message" : message} |
|||
} |
|||
|
|||
func Respond(w http.ResponseWriter, data map[string] interface{}) { |
|||
w.Header().Add("Content-Type", "application/json") |
|||
json.NewEncoder(w).Encode(data) |
|||
} |
|||
Loading…
Reference in new issue