Skip to content
This repository has been archived by the owner on Apr 11, 2023. It is now read-only.

Commit

Permalink
Merge pull request #220 from Moopli/gnap-server
Browse files Browse the repository at this point in the history
feat: httpsig GNAP request proof algorithm
  • Loading branch information
rolsonquadras authored May 18, 2022
2 parents 2540de8 + 2c77c5d commit fe6ea3a
Show file tree
Hide file tree
Showing 28 changed files with 1,578 additions and 201 deletions.
1 change: 1 addition & 0 deletions cmd/auth-rest/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ require (
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.1 // indirect
github.com/hyperledger/aries-framework-go v0.1.8 // indirect
github.com/igor-pavlenko/httpsignatures-go v0.0.23 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions cmd/auth-rest/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ github.com/hyperledger/aries-framework-go/test/component v0.0.0-20220330140627-0
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/igor-pavlenko/httpsignatures-go v0.0.23 h1:b+bo2vox5fwHKGiGfnu9/L5gM6RwSBdKQMPi48scAj4=
github.com/igor-pavlenko/httpsignatures-go v0.0.23/go.mod h1:3LVsCi3evlfQSNDKMTg3uElxEP8SjK3/Q5N9I8GU9W0=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
Expand Down
67 changes: 31 additions & 36 deletions component/gnap/as/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ package as

import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -38,15 +37,15 @@ type Client struct {
// and a base URL of the authorization server.
func NewClient(signer gnap.Signer, httpClient *http.Client, gnapAuthServerURL string) (*Client, error) {
if signer == nil {
return nil, fmt.Errorf("gnap auth client: missing signer")
return nil, fmt.Errorf("missing signer")
}

if httpClient == nil {
return nil, fmt.Errorf("gnap auth client: missing http client")
return nil, fmt.Errorf("missing http client")
}

if gnapAuthServerURL == "" {
return nil, fmt.Errorf("gnap auth client: missing Authorization Server URL")
return nil, fmt.Errorf("missing Authorization Server URL")
}

return &Client{
Expand All @@ -58,21 +57,18 @@ func NewClient(signer gnap.Signer, httpClient *http.Client, gnapAuthServerURL st

// RequestAccess creates a GNAP grant access req then submit it to the server to receive a response with an
// interact_ref value.
func (c *Client) RequestAccess(req *gnap.AuthRequest) (*gnap.AuthResponse, error) {
func (c *Client) RequestAccess(req *gnap.AuthRequest) (*gnap.AuthResponse, error) { // nolint:gocyclo
if req == nil {
return nil, fmt.Errorf("requestAccess: empty request")
return nil, fmt.Errorf("empty request")
}

mReq, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("requestAccess: marshal access token error: %w", err)
if req.Client != nil && !req.Client.IsReference && req.Client.Key != nil {
req.Client.Key.Proof = c.signer.ProofType()
}

var sig []byte

sig, err = c.signer.Sign(mReq)
mReq, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("requestAccess: signature error: %w", err)
return nil, fmt.Errorf("marshal access token error: %w", err)
}

requestReader := bytes.NewReader(mReq)
Expand All @@ -81,16 +77,19 @@ func (c *Client) RequestAccess(req *gnap.AuthRequest) (*gnap.AuthResponse, error

httpReq, err := http.NewRequest(http.MethodPost, url, requestReader) // nolint:noctx
if err != nil {
return nil, fmt.Errorf("requestAccess: failed to build http request: %w", err)
return nil, fmt.Errorf("failed to build http request: %w", err)
}

httpReq.Header.Add("Content-Type", contentType)
// httpReq.Header.Add("Signature-Input", "TODO") // TODO update signature input
httpReq.Header.Add("Signature", base64.URLEncoding.EncodeToString(sig))

httpReq, err = c.signer.Sign(httpReq, mReq)
if err != nil {
return nil, fmt.Errorf("signature error: %w", err)
}

r, err := c.httpClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("requestAccess: failed to post HTTP request to [%s]: %w", gnaprest.AuthRequestPath, err)
return nil, fmt.Errorf("failed to post HTTP request to [%s]: %w", gnaprest.AuthRequestPath, err)
}

defer func() {
Expand All @@ -101,20 +100,20 @@ func (c *Client) RequestAccess(req *gnap.AuthRequest) (*gnap.AuthResponse, error
}()

if r.StatusCode != http.StatusOK {
return nil, fmt.Errorf("requestAccess: Auth server replied with invalid Status [%s]: %v",
return nil, fmt.Errorf("auth server replied with invalid status [%s]: %v",
gnaprest.AuthRequestPath, r.Status)
}

respBody, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, fmt.Errorf("requestAccess: read response failed [%s]: %w", gnaprest.AuthRequestPath, err)
return nil, fmt.Errorf("read response failed [%s]: %w", gnaprest.AuthRequestPath, err)
}

gnapResp := &gnap.AuthResponse{}

err = json.Unmarshal(respBody, gnapResp)
if err != nil {
return nil, fmt.Errorf("requestAccess: read response not properly formatted [%s, %w]",
return nil, fmt.Errorf("read response not properly formatted [%s, %w]",
gnaprest.AuthRequestPath, err)
}

Expand All @@ -124,37 +123,33 @@ func (c *Client) RequestAccess(req *gnap.AuthRequest) (*gnap.AuthResponse, error
// Continue gnap auth request containing interact_ref.
func (c *Client) Continue(req *gnap.ContinueRequest, token string) (*gnap.AuthResponse, error) {
if req == nil {
return nil, fmt.Errorf("continue: empty request")
return nil, fmt.Errorf("empty request")
}

mReq, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("continue: marshal access token error: %w", err)
}

var sig []byte

sig, err = c.signer.Sign(mReq)
if err != nil {
return nil, fmt.Errorf("continue: signature error: %w", err)
return nil, fmt.Errorf("marshal access token error: %w", err)
}

requestReader := bytes.NewReader(mReq)

//nolint:noctx // TODO add context if needed.
httpReq, err := http.NewRequest(http.MethodPost, c.gnapAuthServerURL+gnaprest.AuthContinuePath, requestReader)
if err != nil {
return nil, fmt.Errorf("continue: failed to build http request: %w", err)
return nil, fmt.Errorf("failed to build http request: %w", err)
}

httpReq.Header.Add("Content-Type", contentType)
// httpReq.Header.Add("Signature-Input", "TODO") // TODO update signature input
httpReq.Header.Add("Signature", base64.URLEncoding.EncodeToString(sig))
httpReq.Header.Add("Authorization", "GNAP "+token)

httpReq, err = c.signer.Sign(httpReq, mReq)
if err != nil {
return nil, fmt.Errorf("signature error: %w", err)
}

r, err := c.httpClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("continue: failed to post HTTP request to [%s]: %w", gnaprest.AuthContinuePath, err)
return nil, fmt.Errorf("failed to post HTTP request to [%s]: %w", gnaprest.AuthContinuePath, err)
}

defer func() {
Expand All @@ -165,20 +160,20 @@ func (c *Client) Continue(req *gnap.ContinueRequest, token string) (*gnap.AuthRe
}()

if r.StatusCode != http.StatusOK {
return nil, fmt.Errorf("continue: Auth server replied with invalid Status [%s]: %v",
return nil, fmt.Errorf("auth server replied with invalid status [%s]: %v",
gnaprest.AuthContinuePath, r.Status)
}

respBody, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, fmt.Errorf("continue: read response failed [%s, %w]", gnaprest.AuthContinuePath, err)
return nil, fmt.Errorf("read response failed [%s, %w]", gnaprest.AuthContinuePath, err)
}

gnapResp := &gnap.AuthResponse{}

err = json.Unmarshal(respBody, gnapResp)
if err != nil {
return nil, fmt.Errorf("continue: read response not properly formatted [%s, %w]",
return nil, fmt.Errorf("read response not properly formatted [%s, %w]",
gnaprest.AuthContinuePath, err)
}

Expand Down
Loading

0 comments on commit fe6ea3a

Please sign in to comment.