iris/_examples/authentication/request/main.go
2019-07-05 16:22:20 +03:00

137 lines
5.1 KiB
Go

package main
import (
"io/ioutil"
"github.com/kataras/iris"
"github.com/kataras/iris/crypto"
)
var (
// Change that to your own key.
// Usually you have an ECDSA private key
// per identify, let's say a user, stored in a database
// or somewhere else and you use its public key
// to sign a user's payload and when this client
// wants to use this payload, on another route,
// you verify it comparing the signature of the payload
// with the user's public key.
//
// Use the crypto.MustGenerateKey to generate a random key
// or
// crypto.ParsePrivateKey to convert data or local file to an *ecdsa.PrivateKey.
// and `crypto.ParsePublicKey` if you only have access to the public one.
testPrivateKey = crypto.MustGenerateKey()
testPublicKey = &testPrivateKey.PublicKey
)
type testPayloadStructure struct {
Key string `json:"key"`
Value string `json:"value"`
}
// The Iris crypto package offers
// authentication (with optional encryption in top of) and verification
// of raw []byte data with `crypto.Marshal/Unmarshal` functions
// and JSON payloads with `crypto.SignJSON/VerifyJSON` functions.
//
// Let's use the `SignJSON` and `VerifyJSON` here as an example,
// as this is the most common scenario for a web application.
func main() {
app := iris.New()
app.Post("/auth/json", func(ctx iris.Context) {
ticket, err := crypto.SignJSON(testPrivateKey, ctx.Request().Body)
if err != nil {
ctx.StatusCode(iris.StatusUnprocessableEntity)
return
}
// Send just the signature back
// ctx.WriteString(ticket.Signature)
// or the whole payload + the signature:
ctx.JSON(ticket)
})
app.Post("/verify/json", func(ctx iris.Context) {
var verificatedPayload testPayloadStructure // this can be anything.
// The VerifyJSON excepts the body to be a JSON structure of
// {
// "signature": the generated signature from /auth/json,
// "payload": the JSON client payload
// }
// That is the form of the `crypto.Ticket` structure.
//
// However, you are not limited to use that form, another common practise is to
// have the signature and the payload we need to check in the same string representation
// and for a better security you add encryption in top of it, so an outsider cannot understand what is what.
// Let's say that the signature can be optionally provided by a URL ENCODED parameter
// and the request body is the payload without any encryption
// -
// of course you can pass an GCM type of encryption/decryption as Marshal's and Unmarshal's last input argument,
// see more about this at the iris/crypto/gcm subpackage for ready-to-use solutions.
// -
// So we will check if a url parameter is given, if so we will combine the signature and the body into one slice of bytes
// and we will make use of the `crypto.Unmarshal` instead of the `crypto.VerifyJSON` function
// -
if signature := ctx.URLParam("signature"); signature != "" {
payload, err := ioutil.ReadAll(ctx.Request().Body)
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
return
}
data := append([]byte(signature), payload...)
originalPayloadBytes, ok := crypto.Unmarshal(testPublicKey, data, nil)
if !ok {
ctx.Writef("this does not match, please try again\n")
ctx.StatusCode(iris.StatusUnprocessableEntity)
return
}
ctx.ContentType("application/json")
ctx.Write(originalPayloadBytes)
return
}
ok, err := crypto.VerifyJSON(testPublicKey, ctx.Request().Body, &verificatedPayload)
if err != nil {
ctx.Writef("error on verification: %v\n", err)
ctx.StatusCode(iris.StatusBadRequest)
return
}
if !ok {
ctx.Writef("this does not match, please try again\n")
ctx.StatusCode(iris.StatusUnprocessableEntity)
return
}
// Give back the verificated payload or use it.
ctx.JSON(verificatedPayload)
})
// 1.
// curl -X POST -H "Content-Type: application/json" -d '{"key": "this is a key", "value": "this is a value"}' http://localhost:8080/auth/json
// 2. The result will be something like this:
// {"payload":{"key":"this is a key","value":"this is a value"},"signature":"UgXgbXXvs9nAB3Pg0mG1WR0KBn2KpD/xBIsyOv1o4ZpzKs45hB/yxXiGN1k4Y+mgjdBxP6Gg26qajK6216pAGA=="}
// 3. Copy-paste the whole result and do:
// curl -X POST -H "Content-Type: application/json" -d '{"payload":{"key":"this is a key","value":"this is a value"},"signature":"UgXgbXXvs9nAB3Pg0mG1WR0KBn2KpD/xBIsyOv1o4ZpzKs45hB/yxXiGN1k4Y+mgjdBxP6Gg26qajK6216pAGA=="}' http://localhost:8080/verify/json
// 4. Or pass by ?signature encoded URL parameter:
// curl -X POST -H "Content-Type: application/json" -d '{"key": "this is a key", "value": "this is a value"}' http://localhost:8080/verify/json?signature=UgXgbXXvs9nAB3Pg0mG1WR0KBn2KpD%2FxBIsyOv1o4ZpzKs45hB%2FyxXiGN1k4Y%2BmgjdBxP6Gg26qajK6216pAGA%3D%3D
// 5. At both cases the result should be:
// {"key":"this is a key","value":"this is a value"}
// Otherise the verification failed.
//
// Note that each time server is restarted a new private and public key pair is generated,
// look at the start of the program.
app.Run(iris.Addr(":8080"))
}
// You can read more examples and run testable code
// at the `iris/crypto`, `iris/crypto/sign`
// and `iris/crypto/gcm` packages themselves.