oauth2パッケージ go言語でoauth2を扱うのに便利なoauth2 パッケージがあります。 今回は、これを使ってみたいと思います。 例として、Googleフォト(Picasa)のAPIを扱います。 ソースは下記を参照。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import ( "bufio" "fmt" "io/ioutil" "log" "os" "golang.org/x/oauth2" "golang.org/x/oauth2/google" ) func main () { config := oauth2.Config{ ClientID: "xxxxxxxxxxx" , ClientSecret: "yyyyyyyyyyyy" , Endpoint: google.Endpoint, RedirectURL: "urn:ietf:wg:oauth:2.0:oob" , Scopes: []string {"https://picasaweb.google.com/data/" }, } url := config.AuthCodeURL("test" ) fmt.Println(url) var s string var sc = bufio.NewScanner(os.Stdin) if sc.Scan() { s = sc.Text() } token, err := config.Exchange(oauth2.NoContext, s) if err != nil { log.Fatalf("exchange error" ) } client := config.Client(oauth2.NoContext, token) resp, err := client.Get("https://picasaweb.google.com/data/feed/api/user/default?start-index=1" ) if err != nil { log.Fatalf("client get error" ) } defer resp.Body.Close() byteArray, _ := ioutil.ReadAll(resp.Body) fmt.Println(string (byteArray)) }
どこでoauth2のアクセストークンを扱っているか 上記のソースではトークンを取得した後、config.Client()でhttp Clientを取得してそれを使うだけで、oauth2の通信が行えています。 どこで、アクセストークンをセットし、トークンの期限が切れていたらリフレッシュトークンを使うなどの処理をしているか気になります。 ソースコードを見ていくと、config.Client()の先、NewClient()でhttp.Clientを作成しているのが見えます。 ここで、http.ClientのTransportに独自のTransportを設定しているのがそれっぽいです。
1 2 3 4 5 6 7 8 9 10 func NewClient (ctx context.Context, src TokenSource) *http .Client { return &http.Client{ Transport: &Transport{ Base: internal.ContextTransport(ctx), Source: ReuseTokenSource(nil , src), }, } }
このTransportですが、RoundTripというメソッドを持っています。この辺について詳しくは以下のページを参照。Go http.RoundTripper 実装ガイド RoundTrip内を見ると、トークンが切れていたらリフレッシュしたり、Authorizationヘッダにアクセストークンをつけているのが追えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func (t *Transport) RoundTrip (req *http.Request) (*http.Response, error) { if t.Source == nil { return nil , errors.New("oauth2: Transport's Source is nil" ) } token, err := t.Source.Token() if err != nil { return nil , err } req2 := cloneRequest(req) token.SetAuthHeader(req2) t.setModReq(req, req2) res, err := t.base().RoundTrip(req2) if err != nil { t.setModReq(req, nil ) return nil , err } res.Body = &onEOFReader{ rc: res.Body, fn: func () { t.setModReq(req, nil ) }, } return res, nil }