// Copyright 2017 Drone.IO Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package oauth2 import ( "errors" "net/http" "time" "git.awesome-for.me/liuzhiguo/go-login/login" "git.awesome-for.me/liuzhiguo/go-login/login/logger" ) // Handler returns a Handler that runs h at the completion // of the oauth2 authorization flow. func Handler(h http.Handler, c *Config) http.Handler { return &handler{next: h, conf: c, logs: c.Logger} } type handler struct { conf *Config next http.Handler logs logger.Logger } func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // checks for the error query parameter in the request. // If non-empty, write to the context and proceed with // the next http.Handler in the chain. if erro := r.FormValue("error"); erro != "" { h.logger().Errorf("oauth: authorization error: %s", erro) ctx = login.WithError(ctx, errors.New(erro)) h.next.ServeHTTP(w, r.WithContext(ctx)) return } // checks for the code query parameter in the request // If empty, redirect to the authorization endpoint. code := r.FormValue("code") if len(code) == 0 { state := createState(w) http.Redirect(w, r, h.conf.authorizeRedirect(state), 303) return } // checks for the state query parameter in the requet. // If empty, write the error to the context and proceed // with the next http.Handler in the chain. state := r.FormValue("state") deleteState(w) if err := validateState(r, state); err != nil { h.logger().Errorln("oauth: invalid or missing state") ctx = login.WithError(ctx, err) h.next.ServeHTTP(w, r.WithContext(ctx)) return } // requests the access_token and refresh_token from the // authorization server. If an error is encountered, // write the error to the context and prceed with the // next http.Handler in the chain. source, err := h.conf.exchange(code, state) if err != nil { h.logger().Errorf("oauth: cannot exchange code: %s: %s", code, err) ctx = login.WithError(ctx, err) h.next.ServeHTTP(w, r.WithContext(ctx)) return } // converts the oauth2 token type to the internal Token // type and attaches to the context. ctx = login.WithToken(ctx, &login.Token{ Access: source.AccessToken, Refresh: source.RefreshToken, Expires: time.Now().UTC().Add( time.Duration(source.Expires) * time.Second, ), }) h.next.ServeHTTP(w, r.WithContext(ctx)) } func (h *handler) logger() logger.Logger { if h.logs == nil { return logger.Discard() } return h.logs }