Skip to content

Instantly share code, notes, and snippets.

@1l0
Created October 24, 2020 12:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1l0/7e1e6e4eb7da887dfeb8d2afd80bd059 to your computer and use it in GitHub Desktop.
Save 1l0/7e1e6e4eb7da887dfeb8d2afd80bd059 to your computer and use it in GitHub Desktop.
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