github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/appengine/login/googlesignin/google_signin.go (about)

     1  // package googlesignin offers a login completely based on client javascript;
     2  // signin-signout being messaged accross devices;
     3  // installation of apps can be triggered;
     4  // it has no server side login comparable to appengine/login or appengine/login/gitkit.
     5  package googlesignin
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"log"
    11  	"net/http"
    12  	"strings"
    13  
    14  	"github.com/pbberlin/tools/appengine/login"
    15  	"github.com/pbberlin/tools/appengine/login/googlesignin/jwt-go"
    16  	"github.com/pbberlin/tools/net/http/fetch"
    17  	"github.com/pbberlin/tools/net/http/loghttp"
    18  	"github.com/pbberlin/tools/net/http/routes"
    19  	"github.com/pbberlin/tools/stringspb"
    20  )
    21  
    22  func init() {
    23  	http.HandleFunc("/tokensignin", TokenSignin)
    24  }
    25  
    26  //
    27  // https://developers.google.com/identity/choose-auth
    28  // https://developers.google.com/identity/sign-in/web/backend-auth
    29  func TokenSignin(w http.ResponseWriter, r *http.Request) {
    30  
    31  	lg, _ := loghttp.BuffLoggerUniversal(w, r)
    32  
    33  	// w.Header().Set("Access-Control-Allow-Origin", "http://localhost:1313")
    34  
    35  	w.Header().Set("Access-Control-Allow-Origin", "http://"+routes.AppHostDev())
    36  
    37  	w.Header().Del("Access-Control-Allow-Origin")
    38  	w.Header().Set("Access-Control-Allow-Origin", "*")
    39  
    40  	// err := r.ParseMultipartForm(1024 * 1024 * 2)
    41  	err := r.ParseForm()
    42  	lg(err)
    43  
    44  	myToken := r.Form.Get("idtoken")
    45  	tokSize := fmt.Sprintf("Len of Tok was %v. \n", len(myToken))
    46  
    47  	fc1 := func(token *jwt.Token) (interface{}, error) {
    48  		// Don't forget to validate the alg is what you expect:
    49  
    50  		log.Printf("algo header is %v\n", token.Header["alg"])
    51  		if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
    52  			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
    53  		}
    54  		return token.Header["kid"], nil
    55  	}
    56  
    57  	token, err := jwt.Parse(myToken, fc1)
    58  
    59  	// No direct error comparison possible; since err is wrapped in another struct
    60  	if err != nil && strings.Contains(err.Error(), jwt.ErrPEMMappingObsolete.Error()) {
    61  
    62  		currentPEMsURL := "https://www.googleapis.com/oauth2/v1/certs"
    63  		req, err := http.NewRequest("GET", currentPEMsURL, nil)
    64  		if err != nil {
    65  			lg("creation of pem request failed")
    66  			return
    67  		}
    68  		req.Header.Set("Content-Type", "application/json")
    69  
    70  		fo := fetch.Options{Req: req}
    71  		fo.KnownProtocol = "https"
    72  		fo.ForceHTTPSEvenOnDevelopmentServer = true
    73  		bts, inf, err := fetch.UrlGetter(r, fo)
    74  		lg(err)
    75  		if err != nil {
    76  			lg("tried to fetch %v, %v", currentPEMsURL, inf.URL)
    77  			lg("msg %v", inf.Msg)
    78  			return
    79  		}
    80  		if len(bts) > 200 {
    81  			var data1 map[string]string
    82  			err = json.Unmarshal(bts, &data1)
    83  			lg(err)
    84  			// lg(stringspb.IndentedDumpBytes(data1))
    85  			// w.Write(stringspb.IndentedDumpBytes(data1))
    86  			if len(data1) > 1 {
    87  				lg("PEM mappings updated")
    88  				jwt.MappingToPEM = data1
    89  			} else {
    90  				lg("PEM mapping response contained only %v records; bytes length %v", len(data1), len(bts))
    91  			}
    92  		}
    93  
    94  	}
    95  
    96  	token, err = jwt.Parse(myToken, fc1)
    97  
    98  	if err != nil && strings.Contains(err.Error(), jwt.ErrInvalidKey.Error()) {
    99  		w.Write([]byte("The submitted RSA Key was somehow unparseable. We still accept the token.\n"))
   100  		/*
   101  			https://developers.google.com/identity/sign-in/web/backend-auth
   102  		*/
   103  		err = nil
   104  		token.Valid = true
   105  	}
   106  
   107  	if err != nil {
   108  		w.Write([]byte("--- " + err.Error() + ".\n"))
   109  	}
   110  
   111  	if err == nil && token.Valid {
   112  
   113  		tk := ""
   114  		tk += fmt.Sprintf("     Algor:     %v\n", token.Method)
   115  		tk += fmt.Sprintf("     Header:    %v\n", token.Header)
   116  		for k, v := range token.Claims {
   117  			tk += fmt.Sprintf("\t  %-8v %v\n", k, v)
   118  		}
   119  		lg(tk)
   120  
   121  		w.Write([]byte("tokensignin; valid.   \n"))
   122  		w.Write([]byte(tokSize))
   123  		sb := "header-sub-not-present"
   124  		if _, ok := token.Claims["sub"]; ok {
   125  			sb = token.Claims["sub"].(string)
   126  		}
   127  		w.Write([]byte("ID from PWT is " + sb + "\n"))
   128  
   129  		_, usr, msg1 := login.CheckForNormalUser(r)
   130  		if usr != nil {
   131  			w.Write([]byte("ID from SRV is " + usr.ID + "\n"))
   132  		}
   133  		w.Write([]byte(msg1 + "\n"))
   134  
   135  	} else {
   136  		w.Write([]byte("tokensignin; INVALID. \n"))
   137  		w.Write([]byte(tokSize))
   138  		w.Write([]byte(stringspb.ToLen(myToken, 30)))
   139  
   140  		vrf := fmt.Sprintf("\nhttps://www.googleapis.com/oauth2/v3/tokeninfo?id_token=%v \n", myToken)
   141  		w.Write([]byte(vrf))
   142  	}
   143  
   144  }