Created
October 24, 2020 12:15
-
-
Save 1l0/7e1e6e4eb7da887dfeb8d2afd80bd059 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package api | |
import ( | |
"bytes" | |
"context" | |
"crypto/md5" | |
"encoding/json" | |
"fmt" | |
"io" | |
"log" | |
"net/http" | |
"time" | |
"github.com/gbrlsnchs/jwt/v3" | |
"github.com/google/uuid" | |
) | |
const ( | |
key = "KEY" | |
claims = "JWT_CLAIMS" | |
endpoint = "GRAPHQL_ENDPOINT" | |
issuer = "JWT_ISSUER" | |
aliveDays = 30 | |
) | |
var ( | |
headers = map[string]string{ | |
"Access-Control-Allow-Origin": "*", | |
"Access-Control-Allow-Methods": "OPTIONS, POST", | |
"Access-Control-Allow-Headers": "content-type", | |
"Allow": "OPTIONS, POST", | |
} | |
keyHS = jwt.NewHS256([]byte(key)) | |
) | |
func init() { | |
log.SetFlags(log.Ltime | log.Lshortfile) | |
} | |
type user struct { | |
UserName string `json:"username"` | |
Pass string `json:"pass"` | |
} | |
// Handler ... | |
func Handler(w http.ResponseWriter, r *http.Request) { | |
fillHeader(w, r) | |
if r.Method == "OPTIONS" { | |
w.WriteHeader(http.StatusOK) | |
return | |
} | |
if r.Method != "POST" { | |
http.Error(w, "Method Not Allowed", http.StatusNotAcceptable) | |
return | |
} | |
u := &user{} | |
dec := json.NewDecoder(r.Body) | |
if err := dec.Decode(u); err != nil { | |
http.Error(w, "Internal Error", http.StatusInternalServerError) | |
return | |
} | |
if u.UserName == "" || u.Pass == "" { | |
http.Error(w, "Invalid Request", http.StatusBadRequest) | |
return | |
} | |
token := generateToken(u.UserName, u.Pass) | |
if token == "" { | |
http.Error(w, "Invalid Request", http.StatusBadRequest) | |
return | |
} | |
headers := map[string]string{ | |
"auth": token, | |
"Content-Type": "application/json", | |
} | |
q := new(query) | |
q.Query = ` | |
query ($username: String!){ | |
getUser(username:$username) { | |
username | |
isType | |
isContact { id name } | |
} | |
}` | |
q.Variables.Username = u.UserName | |
b := &bytes.Buffer{} | |
if err := json.NewEncoder(b).Encode(q); err != nil { | |
http.Error(w, "Internal Error", http.StatusInternalServerError) | |
return | |
} | |
resp := fetch(endpoint, headers, b) | |
if resp == nil { | |
http.Error(w, "Invalid Request", http.StatusBadRequest) | |
return | |
} | |
if resp.Data.GetUser.UserName == "" { | |
http.Error(w, "Bad Request", http.StatusBadRequest) | |
return | |
} | |
ur := generateResponse(resp) | |
if err := json.NewEncoder(w).Encode(ur); err != nil { | |
http.Error(w, "Internal Error", http.StatusInternalServerError) | |
return | |
} | |
} | |
func generateToken(name, pass string) string { | |
passMD := fmt.Sprintf("%x", md5.Sum([]byte(pass))) | |
now := time.Now() | |
pl := customPayload{ | |
Password: passMD, | |
UserName: name, | |
Payload: jwt.Payload{ | |
Issuer: issuer, | |
Subject: "token", | |
ExpirationTime: jwt.NumericDate(now.Add(1 * time.Minute)), | |
JWTID: uuid.New().String(), | |
}, | |
} | |
token, _ := jwt.Sign(pl, keyHS) | |
return string(token) | |
} | |
type customPayload struct { | |
jwt.Payload | |
Password string `json:"PASSWORD"` | |
UserName string `json:"USERNAME"` | |
} | |
func generateResponse(r *response) *UserResponse { | |
var adminToken, userToken string | |
if r.Data.GetUser.IsType == "ADMIN" { | |
adminToken = tokenGen(r, true) | |
} | |
userToken = tokenGen(r, false) | |
ur := &UserResponse{ | |
Token: userToken, | |
UserName: r.Data.GetUser.UserName, | |
} | |
if userToken == "" { | |
ur.Error = "Invalid" | |
return ur | |
} | |
if adminToken != "" { | |
ur.AdminToken = adminToken | |
} | |
return ur | |
} | |
// UserResponse ... | |
type UserResponse struct { | |
Token string `json:"token"` | |
AdminToken string `json:"admintoken,omitempty"` | |
UserName string `json:"username"` | |
Error string `json:"error,omitempty"` | |
} | |
func tokenGen(r *response, admin bool) string { | |
now := time.Now() | |
userrole := "" | |
if admin { | |
userrole = "ADMINISTRATOR" | |
} else { | |
userrole = "USER" | |
} | |
pl := responsePayload{ | |
UserName: r.Data.GetUser.UserName, | |
IsContact: r.Data.GetUser.IsContact.ID, | |
UserRole: userrole, | |
Payload: jwt.Payload{ | |
Issuer: issuer, | |
Subject: r.Data.GetUser.UserName, | |
ExpirationTime: jwt.NumericDate(now.Add(30 * 24 * time.Hour)), | |
JWTID: uuid.New().String(), | |
}, | |
} | |
token, _ := jwt.Sign(pl, keyHS) | |
return string(token) | |
} | |
type responsePayload struct { | |
jwt.Payload | |
UserName string `json:"USERNAME"` | |
IsContact string `json:"ISCONTACT"` | |
UserRole string `json:"USERROLE"` | |
} | |
type query struct { | |
Query string `json:"query"` | |
Variables struct { | |
Username string `json:"username"` | |
} `json:"variables"` | |
} | |
type response struct { | |
Data struct { | |
GetUser struct { | |
UserName string `json:"username"` | |
IsType string `json:"isType"` | |
IsContact struct { | |
ID string `json:"id"` | |
} `json:"isContact"` | |
} `json:"getUser"` | |
} `json:"data"` | |
} | |
func fillHeader(w http.ResponseWriter, r *http.Request) { | |
for k, v := range headers { | |
w.Header().Set(k, v) | |
} | |
} | |
func fetch(path string, headers map[string]string, b io.Reader) *response { | |
ctx := context.Background() | |
ctx, cancel := context.WithCancel(ctx) | |
req, err := http.NewRequestWithContext(ctx, "POST", path, b) | |
if err != nil { | |
cancel() | |
return nil | |
} | |
for k, v := range headers { | |
req.Header.Set(k, v) | |
} | |
go func() { | |
time.Sleep(30 * time.Second) | |
cancel() | |
}() | |
resp, err := http.DefaultClient.Do(req) | |
if err != nil { | |
return nil | |
} | |
defer resp.Body.Close() | |
r := new(response) | |
dec := json.NewDecoder(resp.Body) | |
if err := dec.Decode(r); err != nil { | |
return nil | |
} | |
return r | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment