Googleと一緒にOAuth 2


認証は、任意のアプリケーションで最も一般的な部分です.あなた自身の認証システムを実装するか、存在する多くの選択肢のうちの1つを使用することができます、しかし、この場合、我々はOAuS 2を使用するつもりです.
OAuthはOAuth 2 Go hereの詳細については、ユーザーが自分のユーザ名とパスワードをそのサービスで共有することなくデータをアクセスできるようにする仕様です.

設定プロジェクト


まず最初に、Googleプロジェクトを作成し、OAuth 2の資格情報を作成する必要があります.
  • は、Google雲プラットホーム
  • に行きます
  • 新しいプロジェクトを作成するか、すでにそれを選択した場合.
  • 資格証明書に行き、次に「OAuthクライアントID」を選択する新しいものを作成します
  • は、この例のlocalhost:8000/auth/google/callbackのために「認可されたリダイレクトURL」を加えます
  • はCententCard IDとクライアント秘密
  • をコピーします

    OAuth 2の使い方


    承認シーケンスは、アプリケーションがブラウザをGoogle URLにリダイレクトするときに開始されますURLには、要求されているアクセスの種類を示すクエリパラメーターが含まれます.Googleはユーザ認証、セッション選択、ユーザー同意を扱います.結果はアプリケーションがアクセストークンとリフレッシュトークンと交換できる認可コードです.
    アプリケーションは、将来の使用のためにリフレッシュトークンを保存し、Google APIにアクセスするためのアクセストークンを使用します.アクセストークンが期限切れになると、アプリケーションは新しいトークンを取得するためにリフレッシュトークンを使用します.

    コードに行こう


    OAuth 2認証と認証HTTPリクエストのサポートを提供するパッケージ「Golang . org/X/OAuth 2」を使用します.
    あなたのWorddirに新しいプロジェクト(フォルダ)を作成します.私の場合は「oauths 2」と呼びますが、OAutor 2のパッケージを含める必要があります.go get golang.org/x/oauth2だからプロジェクトに我々はメインを作成します.行け.
    package main
    
    import (
        "fmt"
        "net/http"
        "log"
        "github.com/douglasmakey/oauth2-example/handlers"
    )
    
    func main() {
        server := &http.Server{
            Addr: fmt.Sprintf(":8000"),
            Handler: handlers.New(),
        }
    
        log.Printf("Starting HTTP Server. Listening at %q", server.Addr)
        if err := server.ListenAndServe(); err != http.ErrServerClosed {
            log.Printf("%v", err)
        } else {
            log.Println("Server closed!")
        }
    }
    
    
    HTTPを使用して簡単なサーバーを作成します.サーバーと実行.
    次に、このフォルダの「ベース」を作成します.どうぞ.
    package handlers
    
    import (
        "net/http"
    )
    
    func New() http.Handler {
        mux := http.NewServeMux()
        // Root
        mux.Handle("/",  http.FileServer(http.Dir("templates/")))
    
        // OauthGoogle
        mux.HandleFunc("/auth/google/login", oauthGoogleLogin)
        mux.HandleFunc("/auth/google/callback", oauthGoogleCallback)
    
        return mux
    }
    
    HTTPを使用します.私たちのエンドポイントを処理するためのServemuxは、次のルートエンドポイント“/”を作成します.HTTPこのファイルは' index 'です.HTML 'とフォルダ'テンプレート'です.
    また、Google Auth\/Auth/Google/login "と"\/auth/Google/callback "でOAuth用の2つのエンドポイントを作成します.我々はGoogleコンソールでアプリケーションを設定するときに覚えていますか?コールバックURLは同じでなければなりません.
    次に、ハンドラに別のファイルを作成します.移動してください、このファイルは、我々のアプリケーションでGoogleでOAuthを扱うすべてのロジックを含みます.
    VAR GoogleOAuconfigをAuthで宣言します.Googleと通信する設定.
    スコープ:OAuth 2.0スコープは、アクセストークンに与えられるアクセス量を制限する方法を提供します.
    var googleOauthConfig = &oauth2.Config{
        RedirectURL:  "http://localhost:8000/auth/google/callback",
        ClientID:     os.Getenv("GOOGLE_OAUTH_CLIENT_ID"),
        ClientSecret: os.Getenv("GOOGLE_OAUTH_CLIENT_SECRET"),
        Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email"},
        Endpoint:     google.Endpoint,
    }
    

    ハンドラー


    このハンドラはログインリンクを作成し、ユーザをリダイレクトします.
    AuthCodeURL CSRF攻撃からユーザーを保護するためのトークンである状態を受け取ります.常に空の文字列を提供し、リダイレクトコールバックの状態クエリパラメーターと一致することを検証する必要があります.これは、各リクエストに対してランダムに生成されることをお勧めします.
    func oauthGoogleLogin(w http.ResponseWriter, r *http.Request) {
    
        // Create oauthState cookie
        oauthState := generateStateOauthCookie(w)
        u := googleOauthConfig.AuthCodeURL(oauthState)
        http.Redirect(w, r, u, http.StatusTemporaryRedirect)
    }
    
    func generateStateOauthCookie(w http.ResponseWriter) string {
        var expiration = time.Now().Add(365 * 24 * time.Hour)
    
        b := make([]byte, 16)
        rand.Read(b)
        state := base64.URLEncoding.EncodeToString(b)
        cookie := http.Cookie{Name: "oauthstate", Value: state, Expires: expiration}
        http.SetCookie(w, &cookie)
    
        return state
    }
    
    

    ハンドラハンドラ


    このハンドラは、状態がOAuthEstateCookieに等しいかどうかをチェックし、その関数をGetGetDataFromGoogle関数に渡します.
    func oauthGoogleCallback(w http.ResponseWriter, r *http.Request) {
        // Read oauthState from Cookie
        oauthState, _ := r.Cookie("oauthstate")
    
        if r.FormValue("state") != oauthState.Value {
            log.Println("invalid oauth google state")
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }
    
        data, err := getUserDataFromGoogle(r.FormValue("code"))
        if err != nil {
            log.Println(err.Error())
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }
    
        // GetOrCreate User in your db.
        // Redirect or response with a token.
        // More code .....
        fmt.Fprintf(w, "UserInfo: %s\n", data)
    }
    
    func getUserDataFromGoogle(code string) ([]byte, error) {
        // Use code to get token and get user info from Google.
    
        token, err := googleOauthConfig.Exchange(context.Background(), code)
        if err != nil {
            return nil, fmt.Errorf("code exchange wrong: %s", err.Error())
        }
        response, err := http.Get(oauthGoogleUrlAPI + token.AccessToken)
        if err != nil {
            return nil, fmt.Errorf("failed getting user info: %s", err.Error())
        }
        defer response.Body.Close()
        contents, err := ioutil.ReadAll(response.Body)
        if err != nil {
            return nil, fmt.Errorf("failed read response: %s", err.Error())
        }
        return contents, nil
    }
    
    
    

    完全なコードOAuthMen Google。試み


    package handlers
    
    import (
        "golang.org/x/oauth2"
        "golang.org/x/oauth2/google"
        "net/http"
        "fmt"
        "io/ioutil"
        "context"
        "log"
        "encoding/base64"
        "crypto/rand"
        "os"
        "time"
    )
    
    // Scopes: OAuth 2.0 scopes provide a way to limit the amount of access that is granted to an access token.
    var googleOauthConfig = &oauth2.Config{
        RedirectURL:  "http://localhost:8000/auth/google/callback",
        ClientID:     os.Getenv("GOOGLE_OAUTH_CLIENT_ID"),
        ClientSecret: os.Getenv("GOOGLE_OAUTH_CLIENT_SECRET"),
        Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email"},
        Endpoint:     google.Endpoint,
    }
    
    const oauthGoogleUrlAPI = "https://www.googleapis.com/oauth2/v2/userinfo?access_token="
    
    func oauthGoogleLogin(w http.ResponseWriter, r *http.Request) {
    
        // Create oauthState cookie
        oauthState := generateStateOauthCookie(w)
    
        /*
        AuthCodeURL receive state that is a token to protect the user from CSRF attacks. You must always provide a non-empty string and
        validate that it matches the the state query parameter on your redirect callback.
        */
        u := googleOauthConfig.AuthCodeURL(oauthState)
        http.Redirect(w, r, u, http.StatusTemporaryRedirect)
    }
    
    func oauthGoogleCallback(w http.ResponseWriter, r *http.Request) {
        // Read oauthState from Cookie
        oauthState, _ := r.Cookie("oauthstate")
    
        if r.FormValue("state") != oauthState.Value {
            log.Println("invalid oauth google state")
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }
    
        data, err := getUserDataFromGoogle(r.FormValue("code"))
        if err != nil {
            log.Println(err.Error())
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }
    
        // GetOrCreate User in your db.
        // Redirect or response with a token.
        // More code .....
        fmt.Fprintf(w, "UserInfo: %s\n", data)
    }
    
    func generateStateOauthCookie(w http.ResponseWriter) string {
        var expiration = time.Now().Add(365 * 24 * time.Hour)
    
        b := make([]byte, 16)
        rand.Read(b)
        state := base64.URLEncoding.EncodeToString(b)
        cookie := http.Cookie{Name: "oauthstate", Value: state, Expires: expiration}
        http.SetCookie(w, &cookie)
    
        return state
    }
    
    func getUserDataFromGoogle(code string) ([]byte, error) {
        // Use code to get token and get user info from Google.
    
        token, err := googleOauthConfig.Exchange(context.Background(), code)
        if err != nil {
            return nil, fmt.Errorf("code exchange wrong: %s", err.Error())
        }
        response, err := http.Get(oauthGoogleUrlAPI + token.AccessToken)
        if err != nil {
            return nil, fmt.Errorf("failed getting user info: %s", err.Error())
        }
        defer response.Body.Close()
        contents, err := ioutil.ReadAll(response.Body)
        if err != nil {
            return nil, fmt.Errorf("failed read response: %s", err.Error())
        }
        return contents, nil
    }
    
    

    走りましょう


    go run main.go
    
    コードRepoでのリポジトリ