Skip to content

Commit

Permalink
Merge pull request #10 from emahiro/feature/rm_local_acccesstoken
Browse files Browse the repository at this point in the history
ローカルのアクセストークンの取得実装を削除する
  • Loading branch information
Hiromichi Ema authored Oct 30, 2019
2 parents c36ba1d + dcb2b84 commit c73770a
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 51 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ go get github.com/emahiro/aehcl
```go

client := &http.Client {
Transport: aehcl.Transport(http.DefaultTransport)
Transport: aehcl.Transport(http.DefaultTransport, aehcl.WithTokenSource(aehcl.FetchIDToken))
}

```
Expand Down
52 changes: 37 additions & 15 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,45 @@ import (
"net/http"
)

// TokenSource is function that returns token required service-to-service authentication in App Engine.
type TokenSource func() (string, error)

// Option is function that adds transport option required service-to-service authentication.
type Option func(*option)

type option struct {
token TokenSource
}

// WithTokenSource sets token source required service-to-service authentication to transport option.
func WithTokenSource(ts TokenSource) Option {
return func(o *option) {
o.token = ts
}
}

func (o *option) apply(opts []Option) {
for _, opt := range opts {
opt(o)
}
}

type transport struct {
base http.RoundTripper
base http.RoundTripper
token TokenSource
}

// Transport is an implementation of http.RoundTripper for App Engine.
// Transport is an implementation of http.RoundTripper for service-to-service authentication.
// When required service-to-service authentication, create http.Client using this transport.
// If base http RoundTripper is nil, it sets http.DefaultTransport.
func Transport(base http.RoundTripper) http.RoundTripper {
//
// Default RoundTripper is http.DefaultTransport, and FetchIDToken is assigned as default option.
func Transport(base http.RoundTripper, opts ...Option) http.RoundTripper {
opt := &option{token: FetchIDToken}
opt.apply(opts)

t := &transport{
base: base,
base: base,
token: opt.token,
}
if base == nil {
t.base = http.DefaultTransport
Expand All @@ -24,13 +53,10 @@ func Transport(base http.RoundTripper) http.RoundTripper {

// RoundTrip issues a request with identity token required service-to-service authentication described in
// https://cloud.google.com/run/docs/authenticating/service-to-service.
// When failed to obtain the identity token from metadata API (e.g. in local environment), uses access token generated
// from service account credentials.
// When failed to obtain the identity token from metadata API (e.g. in local environment), RoundTrip returns error.
//
// If uses service-to-serivce authentication, server that receives the request must be implemented to validate the token
// added to Authorization header.
// In case of identity token, verify the identity token using the public key provided by Google.
// In case of access token, check the access token has permission to execute some operation requested by the receiver.
// If uses service-to-serivce authentication, server that receives the request must be implemented to validate the
// identity token added to Authorization header using the public key provided by Google.
func (t *transport) RoundTrip(ireq *http.Request) (*http.Response, error) {
token, err := t.token()
if err != nil {
Expand All @@ -49,10 +75,6 @@ func (t *transport) RoundTrip(ireq *http.Request) (*http.Response, error) {
return t.base.RoundTrip(req)
}

func (t *transport) token() (string, error) {
return fetchToken()
}

func cloneHeader(h http.Header) http.Header {
nv := 0
for _, v := range h {
Expand Down
11 changes: 10 additions & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ func TestRoundTrip(t *testing.T) {
}{
{
name: "success to get authorization header",
arg: Transport(http.DefaultTransport),
arg: Transport(http.DefaultTransport, WithTokenSource(FetchIDToken)),
handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if h := r.Header.Get("Authorization"); h == "" {
t.Fatalf("Authorization Header is required")
}
}),
},
{
name: "success to get authorization header in empty options",
arg: Transport(http.DefaultTransport, []Option{}...),
handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if h := r.Header.Get("Authorization"); h == "" {
t.Fatalf("Authorization Header is required")
Expand Down
36 changes: 2 additions & 34 deletions identity.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,12 @@
package aehcl

import (
"context"
"fmt"
"os"

"cloud.google.com/go/compute/metadata"
"golang.org/x/oauth2/google"
)

func fetchToken() (string, error) {
// fetch idToken from metadata of gcp
if idt, err := fetchIDToken(); err == nil {
return idt, nil
}

// fetch accesstoken from local `GOOGLE_APPLICATION_CREDENTIALS`
lat, err := fetchLocalAccessToken()
if err != nil {
return "", err
}
return lat, nil
}

func fetchIDToken() (string, error) {
// FetchIDToken returns identity token from metadata API.
func FetchIDToken() (string, error) {
return metadata.Get("/instance/service-accounts/default/identity?audience=" + os.Getenv("GOOGLE_CLOUD_PROJECT"))
}

func fetchLocalAccessToken() (string, error) {
creds, err := google.FindDefaultCredentials(context.Background())
if err != nil {
return "", fmt.Errorf("failed to find default credentials. err: %v", err)

}

token, err := creds.TokenSource.Token()
if err != nil {
return "", fmt.Errorf("failed to fetch token. err: %v", err)

}

return token.AccessToken, nil
}

0 comments on commit c73770a

Please sign in to comment.