Redesign authentication protocol
This commit is contained in:
@ -2,10 +2,10 @@ package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@ -25,6 +25,7 @@ var (
|
||||
ErrUnexpectedResponse = errors.New("unexpected response")
|
||||
ErrUnauthorized = errors.New("unauthorized")
|
||||
ErrRejected = errors.New("rejected")
|
||||
ErrInvalidServerToken = errors.New("invalid server token")
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
@ -40,18 +41,15 @@ func (c *Client) Advertise(attrs peering.PeerAttributes) error {
|
||||
}
|
||||
|
||||
data := &peering.AdvertisingRequest{
|
||||
ID: c.options.PeerID,
|
||||
Attributes: attrs,
|
||||
PublicKey: publicKey,
|
||||
}
|
||||
|
||||
req, _, err := c.createPostRequest(url, data)
|
||||
res, err := c.Post(url, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := c.options.HTTPClient.Do(req)
|
||||
|
||||
switch res.StatusCode {
|
||||
case http.StatusCreated:
|
||||
return nil
|
||||
@ -111,7 +109,7 @@ func (c *Client) Get(url string) (*http.Response, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.signRequest(req, nil); err != nil {
|
||||
if err := c.addClientToken(req, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.options.HTTPClient.Do(req)
|
||||
@ -122,7 +120,7 @@ func (c *Client) Post(url string, data interface{}) (*http.Response, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.signRequest(req, body); err != nil {
|
||||
if err := c.addClientToken(req, body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.options.HTTPClient.Do(req)
|
||||
@ -144,16 +142,22 @@ func (c *Client) createPostRequest(url string, data interface{}) (*http.Request,
|
||||
return req, body, nil
|
||||
}
|
||||
|
||||
func (c *Client) signRequest(r *http.Request, body []byte) error {
|
||||
func (c *Client) addServerToken(r *http.Request) {
|
||||
r.Header.Set(
|
||||
server.ServerTokenHeader,
|
||||
c.options.ServerToken,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) addClientToken(r *http.Request, body []byte) error {
|
||||
bodySum, err := c.createBodySum(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, peering.PeerClaims{
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, peering.ClientTokenClaims{
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
NotBefore: time.Now().Unix(),
|
||||
Issuer: string(c.options.PeerID),
|
||||
ExpiresAt: time.Now().Add(time.Minute * 10).Unix(),
|
||||
},
|
||||
BodySum: bodySum,
|
||||
@ -164,7 +168,9 @@ func (c *Client) signRequest(r *http.Request, body []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Header.Set("Authorization", fmt.Sprintf("%s %s", server.AuthorizationType, tokenStr))
|
||||
r.Header.Set(server.ClientTokenHeader, tokenStr)
|
||||
|
||||
c.addServerToken(r)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -181,6 +187,27 @@ func (c *Client) createBodySum(body []byte) ([]byte, error) {
|
||||
return sha.Sum(nil), nil
|
||||
}
|
||||
|
||||
func (c *Client) PeerID(serverPublicKey *rsa.PublicKey) (peering.PeerID, error) {
|
||||
token, err := jwt.ParseWithClaims(
|
||||
c.options.ServerToken,
|
||||
&peering.ServerTokenClaims{},
|
||||
func(token *jwt.Token) (interface{}, error) {
|
||||
return serverPublicKey, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !token.Valid {
|
||||
return "", ErrInvalidServerToken
|
||||
}
|
||||
serverClaims, ok := token.Claims.(*peering.ServerTokenClaims)
|
||||
if !ok {
|
||||
return "", ErrInvalidServerToken
|
||||
}
|
||||
return serverClaims.PeerID, nil
|
||||
}
|
||||
|
||||
func New(funcs ...OptionFunc) *Client {
|
||||
options := createOptions(funcs...)
|
||||
return &Client{options}
|
||||
|
44
client/client_test.go
Normal file
44
client/client_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"testing"
|
||||
|
||||
peeringCrypto "forge.cadoles.com/wpetit/go-http-peering/crypto"
|
||||
|
||||
peering "forge.cadoles.com/wpetit/go-http-peering"
|
||||
)
|
||||
|
||||
func TestClientPeerID(t *testing.T) {
|
||||
|
||||
serverPK := mustGeneratePrivateKey()
|
||||
peerID := peering.NewPeerID()
|
||||
|
||||
serverToken, err := peeringCrypto.CreateServerToken(serverPK, "test", peerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
client := New(
|
||||
WithServerToken(serverToken),
|
||||
)
|
||||
|
||||
clientPeerID, err := client.PeerID(&serverPK.PublicKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if g, e := clientPeerID, peerID; g != e {
|
||||
t.Errorf("client.PeerID(): got '%v', expected '%v'", g, e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func mustGeneratePrivateKey() *rsa.PrivateKey {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return privateKey
|
||||
}
|
@ -3,22 +3,24 @@ package client
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"net/http"
|
||||
|
||||
peering "forge.cadoles.com/wpetit/go-http-peering"
|
||||
)
|
||||
|
||||
type HTTPClient interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
PeerID peering.PeerID
|
||||
HTTPClient *http.Client
|
||||
BaseURL string
|
||||
PrivateKey *rsa.PrivateKey
|
||||
HTTPClient HTTPClient
|
||||
BaseURL string
|
||||
PrivateKey *rsa.PrivateKey
|
||||
ServerToken string
|
||||
}
|
||||
|
||||
type OptionFunc func(*Options)
|
||||
|
||||
func WithPeerID(id peering.PeerID) OptionFunc {
|
||||
func WithServerToken(token string) OptionFunc {
|
||||
return func(opts *Options) {
|
||||
opts.PeerID = id
|
||||
opts.ServerToken = token
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +30,7 @@ func WithPrivateKey(pk *rsa.PrivateKey) OptionFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func WithHTTPClient(client *http.Client) OptionFunc {
|
||||
func WithHTTPClient(client HTTPClient) OptionFunc {
|
||||
return func(opts *Options) {
|
||||
opts.HTTPClient = client
|
||||
}
|
||||
@ -43,7 +45,6 @@ func WithBaseURL(url string) OptionFunc {
|
||||
func defaultOptions() *Options {
|
||||
return &Options{
|
||||
HTTPClient: http.DefaultClient,
|
||||
PeerID: peering.NewPeerID(),
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user